From df9ce672278a097c435006d29bb1c2a057e4e94e Mon Sep 17 00:00:00 2001 From: Steven Van Ingelgem Date: Tue, 10 Jun 2025 08:37:42 +0200 Subject: [PATCH 1/5] Allow to fetch tags. --- src/git-command-manager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/git-command-manager.ts b/src/git-command-manager.ts index 8e42a38..261aedc 100644 --- a/src/git-command-manager.ts +++ b/src/git-command-manager.ts @@ -261,8 +261,8 @@ class GitCommandManager { } ): Promise { const args = ['-c', 'protocol.version=2', 'fetch'] - if (!refSpec.some(x => x === refHelper.tagsRefSpec) && !options.fetchTags) { - args.push('--no-tags') + if (!refSpec.some(x => x === refHelper.tagsRefSpec)) { + args.push(options.fetchTags ? '--tags' : '--no-tags') } args.push('--prune', '--no-recurse-submodules') From 6b47c9436e478eec2220e61972b035623a83c11e Mon Sep 17 00:00:00 2001 From: Steven Van Ingelgem Date: Tue, 10 Jun 2025 08:45:30 +0200 Subject: [PATCH 2/5] Allow to fetch tags. --- __test__/git-command-manager.test.ts | 3 +++ dist/index.js | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/__test__/git-command-manager.test.ts b/__test__/git-command-manager.test.ts index cea73d4..9b12027 100644 --- a/__test__/git-command-manager.test.ts +++ b/__test__/git-command-manager.test.ts @@ -134,6 +134,7 @@ describe('Test fetchDepth and fetchTags options', () => { '-c', 'protocol.version=2', 'fetch', + '--tags', '--prune', '--no-recurse-submodules', '--filter=filterValue', @@ -248,6 +249,7 @@ describe('Test fetchDepth and fetchTags options', () => { '-c', 'protocol.version=2', 'fetch', + '--tags', '--prune', '--no-recurse-submodules', '--filter=filterValue', @@ -364,6 +366,7 @@ describe('Test fetchDepth and fetchTags options', () => { '-c', 'protocol.version=2', 'fetch', + '--tags', '--prune', '--no-recurse-submodules', '--progress', diff --git a/dist/index.js b/dist/index.js index b0db713..009a26b 100644 --- a/dist/index.js +++ b/dist/index.js @@ -653,8 +653,8 @@ class GitCommandManager { fetch(refSpec, options) { return __awaiter(this, void 0, void 0, function* () { const args = ['-c', 'protocol.version=2', 'fetch']; - if (!refSpec.some(x => x === refHelper.tagsRefSpec) && !options.fetchTags) { - args.push('--no-tags'); + if (!refSpec.some(x => x === refHelper.tagsRefSpec)) { + args.push(options.fetchTags ? '--tags' : '--no-tags'); } args.push('--prune', '--no-recurse-submodules'); if (options.showProgress) { From f3e6cc288df3938c4af7657194799ee8a7a4aff3 Mon Sep 17 00:00:00 2001 From: Steven Van Ingelgem Date: Tue, 10 Jun 2025 09:24:38 +0200 Subject: [PATCH 3/5] Ignore intellij --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index cd1f03c..5f16633 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ __test__/_temp _temp/ lib/ node_modules/ -.vscode/ \ No newline at end of file +.vscode/ +.idea/ From 49528c1b57f74392481cee840cc1374349293aff Mon Sep 17 00:00:00 2001 From: Steven Van Ingelgem Date: Tue, 10 Jun 2025 09:24:51 +0200 Subject: [PATCH 4/5] Proper 2.48 tags handling --- src/git-command-manager.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/git-command-manager.ts b/src/git-command-manager.ts index 261aedc..2d1303b 100644 --- a/src/git-command-manager.ts +++ b/src/git-command-manager.ts @@ -261,8 +261,12 @@ class GitCommandManager { } ): Promise { const args = ['-c', 'protocol.version=2', 'fetch'] - if (!refSpec.some(x => x === refHelper.tagsRefSpec)) { - args.push(options.fetchTags ? '--tags' : '--no-tags') + const hasTagsRefSpec = refSpec.some(x => x === refHelper.tagsRefSpec) + const needsSeparateTagFetch = this.gitVersion.toString().startsWith('2.48') && options.fetchTags && !hasTagsRefSpec + + if (!hasTagsRefSpec) { + // For git 2.48, skip --tags here if we need separate fetch + args.push(needsSeparateTagFetch || !options.fetchTags ? '--no-tags' : '--tags') } args.push('--prune', '--no-recurse-submodules') @@ -293,6 +297,19 @@ class GitCommandManager { await retryHelper.execute(async () => { await that.execGit(args) }) + + // Separate tag fetch for git 2.48 + if (needsSeparateTagFetch) { + const tagArgs = ['-c', 'protocol.version=2', 'fetch', '--tags', '--prune'] + if (options.showProgress) { + tagArgs.push('--progress') + } + tagArgs.push('origin') + + await retryHelper.execute(async () => { + await that.execGit(tagArgs) + }) + } } async getDefaultBranch(repositoryUrl: string): Promise { From 0548471950d86b19eae6c063105707c831fb5c03 Mon Sep 17 00:00:00 2001 From: Steven Van Ingelgem Date: Tue, 10 Jun 2025 09:26:54 +0200 Subject: [PATCH 5/5] Updated tests for the differences in 2.48 behaviour --- __test__/git-command-manager.test.ts | 222 +++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) diff --git a/__test__/git-command-manager.test.ts b/__test__/git-command-manager.test.ts index 9b12027..dbe0c4e 100644 --- a/__test__/git-command-manager.test.ts +++ b/__test__/git-command-manager.test.ts @@ -379,3 +379,225 @@ describe('Test fetchDepth and fetchTags options', () => { ) }) }) + +describe('Test git 2.48 tag fetching behavior', () => { + beforeEach(async () => { + jest.spyOn(fshelper, 'fileExistsSync').mockImplementation(jest.fn()) + jest.spyOn(fshelper, 'directoryExistsSync').mockImplementation(jest.fn()) + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + it('should perform separate tag fetch for git 2.48 when fetchTags is true', async () => { + mockExec.mockImplementation((path, args, options) => { + if (args.includes('version')) { + options.listeners.stdout(Buffer.from('2.48.1')) + } + return 0 + }) + jest.spyOn(exec, 'exec').mockImplementation(mockExec) + + const workingDirectory = 'test' + const lfs = false + const doSparseCheckout = false + git = await commandManager.createCommandManager( + workingDirectory, + lfs, + doSparseCheckout + ) + + const refSpec = ['refspec1'] + const options = { + fetchTags: true + } + + await git.fetch(refSpec, options) + + // First call: main fetch with --no-tags + expect(mockExec).toHaveBeenNthCalledWith( + 2, // First call is version check + expect.any(String), + [ + '-c', + 'protocol.version=2', + 'fetch', + '--no-tags', + '--prune', + '--no-recurse-submodules', + 'origin', + 'refspec1' + ], + expect.any(Object) + ) + + // Second call: separate tag fetch + expect(mockExec).toHaveBeenNthCalledWith( + 3, + expect.any(String), + [ + '-c', + 'protocol.version=2', + 'fetch', + '--tags', + '--prune', + 'origin' + ], + expect.any(Object) + ) + + expect(mockExec).toHaveBeenCalledTimes(3) // version + main fetch + tag fetch + }) + + it('should perform separate tag fetch with progress for git 2.48', async () => { + mockExec.mockImplementation((path, args, options) => { + if (args.includes('version')) { + options.listeners.stdout(Buffer.from('2.48.0')) + } + return 0 + }) + jest.spyOn(exec, 'exec').mockImplementation(mockExec) + + const workingDirectory = 'test' + const lfs = false + const doSparseCheckout = false + git = await commandManager.createCommandManager( + workingDirectory, + lfs, + doSparseCheckout + ) + + const refSpec = ['refspec1'] + const options = { + fetchTags: true, + showProgress: true + } + + await git.fetch(refSpec, options) + + // Main fetch with --no-tags and --progress + expect(mockExec).toHaveBeenNthCalledWith( + 2, + expect.any(String), + [ + '-c', + 'protocol.version=2', + 'fetch', + '--no-tags', + '--prune', + '--no-recurse-submodules', + '--progress', + 'origin', + 'refspec1' + ], + expect.any(Object) + ) + + // Separate tag fetch with --progress + expect(mockExec).toHaveBeenNthCalledWith( + 3, + expect.any(String), + [ + '-c', + 'protocol.version=2', + 'fetch', + '--tags', + '--prune', + '--progress', + 'origin' + ], + expect.any(Object) + ) + }) + + it('should NOT perform separate tag fetch for git 2.48 when fetchTags is false', async () => { + mockExec.mockImplementation((path, args, options) => { + if (args.includes('version')) { + options.listeners.stdout(Buffer.from('2.48.1')) + } + return 0 + }) + jest.spyOn(exec, 'exec').mockImplementation(mockExec) + + const workingDirectory = 'test' + const lfs = false + const doSparseCheckout = false + git = await commandManager.createCommandManager( + workingDirectory, + lfs, + doSparseCheckout + ) + + const refSpec = ['refspec1'] + const options = { + fetchTags: false + } + + await git.fetch(refSpec, options) + + // Only one fetch call with --no-tags + expect(mockExec).toHaveBeenNthCalledWith( + 2, + expect.any(String), + [ + '-c', + 'protocol.version=2', + 'fetch', + '--no-tags', + '--prune', + '--no-recurse-submodules', + 'origin', + 'refspec1' + ], + expect.any(Object) + ) + + expect(mockExec).toHaveBeenCalledTimes(2) // version + single fetch only + }) + + it('should use normal behavior for non-2.48 git versions', async () => { + mockExec.mockImplementation((path, args, options) => { + if (args.includes('version')) { + options.listeners.stdout(Buffer.from('2.47.0')) + } + return 0 + }) + jest.spyOn(exec, 'exec').mockImplementation(mockExec) + + const workingDirectory = 'test' + const lfs = false + const doSparseCheckout = false + git = await commandManager.createCommandManager( + workingDirectory, + lfs, + doSparseCheckout + ) + + const refSpec = ['refspec1'] + const options = { + fetchTags: true + } + + await git.fetch(refSpec, options) + + // Single fetch with --tags + expect(mockExec).toHaveBeenNthCalledWith( + 2, + expect.any(String), + [ + '-c', + 'protocol.version=2', + 'fetch', + '--tags', + '--prune', + '--no-recurse-submodules', + 'origin', + 'refspec1' + ], + expect.any(Object) + ) + + expect(mockExec).toHaveBeenCalledTimes(2) // version + single fetch only + }) +})