diff --git a/README.md b/README.md
index 745ee8a..9104f8b 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
 
 This action checks-out your repository under `$GITHUB_WORKSPACE`, so your workflow can access it.
 
-Only a single commit is fetched by default, for the ref/SHA that triggered the workflow. Set `fetch-depth` to fetch more history. Refer [here](https://help.github.com/en/articles/events-that-trigger-workflows) to learn which commit `$GITHUB_SHA` points to for different events.
+Only a single commit is fetched by default, for the ref/SHA that triggered the workflow. Set `fetch-depth: 0` to fetch all history for all branches and tags. Refer [here](https://help.github.com/en/articles/events-that-trigger-workflows) to learn which commit `$GITHUB_SHA` points to for different events.
 
 The auth token is persisted in the local git config. This enables your scripts to run authenticated git commands. The token is removed during post-job cleanup. Set `persist-credentials: false` to opt-out.
 
@@ -110,6 +110,7 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
 
 # Scenarios
 
+- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches)
 - [Checkout a different branch](#Checkout-a-different-branch)
 - [Checkout HEAD^](#Checkout-HEAD)
 - [Checkout multiple repos (side by side)](#Checkout-multiple-repos-side-by-side)
@@ -117,9 +118,14 @@ Refer [here](https://github.com/actions/checkout/blob/v1/README.md) for previous
 - [Checkout multiple repos (private)](#Checkout-multiple-repos-private)
 - [Checkout pull request HEAD commit instead of merge commit](#Checkout-pull-request-HEAD-commit-instead-of-merge-commit)
 - [Checkout pull request on closed event](#Checkout-pull-request-on-closed-event)
-- [Fetch all tags](#Fetch-all-tags)
-- [Fetch all branches](#Fetch-all-branches)
-- [Fetch all history for all tags and branches](#Fetch-all-history-for-all-tags-and-branches)
+
+## Fetch all history for all tags and branches
+
+```yaml
+- uses: actions/checkout@v2
+  with:
+    fetch-depth: 0
+```
 
 ## Checkout a different branch
 
@@ -207,29 +213,6 @@ jobs:
       - uses: actions/checkout@v2
 ```
 
-## Fetch all tags
-
-```yaml
-- uses: actions/checkout@v2
-- run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
-```
-
-## Fetch all branches
-
-```yaml
-- uses: actions/checkout@v2
-- run: |
-    git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/*
-```
-
-## Fetch all history for all tags and branches
-
-```yaml
-- uses: actions/checkout@v2
-- run: |
-    git fetch --prune --unshallow --tags
-```
-
 # License
 
 The scripts and documentation in this project are released under the [MIT License](LICENSE)
diff --git a/__test__/git-auth-helper.test.ts b/__test__/git-auth-helper.test.ts
index 1d5c3d5..92a462a 100644
--- a/__test__/git-auth-helper.test.ts
+++ b/__test__/git-auth-helper.test.ts
@@ -722,9 +722,11 @@ async function setup(testName: string): Promise<void> {
     log1: jest.fn(),
     remoteAdd: jest.fn(),
     removeEnvironmentVariable: jest.fn((name: string) => delete git.env[name]),
+    revParse: jest.fn(),
     setEnvironmentVariable: jest.fn((name: string, value: string) => {
       git.env[name] = value
     }),
+    shaExists: jest.fn(),
     submoduleForeach: jest.fn(async () => {
       return ''
     }),
diff --git a/__test__/git-directory-helper.test.ts b/__test__/git-directory-helper.test.ts
index c39a2a5..7283102 100644
--- a/__test__/git-directory-helper.test.ts
+++ b/__test__/git-directory-helper.test.ts
@@ -9,6 +9,7 @@ const testWorkspace = path.join(__dirname, '_temp', 'git-directory-helper')
 let repositoryPath: string
 let repositoryUrl: string
 let clean: boolean
+let ref: string
 let git: IGitCommandManager
 
 describe('git-directory-helper tests', () => {
@@ -41,7 +42,8 @@ describe('git-directory-helper tests', () => {
       git,
       repositoryPath,
       repositoryUrl,
-      clean
+      clean,
+      ref
     )
 
     // Assert
@@ -63,7 +65,8 @@ describe('git-directory-helper tests', () => {
       git,
       repositoryPath,
       repositoryUrl,
-      clean
+      clean,
+      ref
     )
 
     // Assert
@@ -88,7 +91,8 @@ describe('git-directory-helper tests', () => {
       git,
       repositoryPath,
       repositoryUrl,
-      clean
+      clean,
+      ref
     )
 
     // Assert
@@ -109,7 +113,8 @@ describe('git-directory-helper tests', () => {
       git,
       repositoryPath,
       repositoryUrl,
-      clean
+      clean,
+      ref
     )
 
     // Assert
@@ -137,7 +142,8 @@ describe('git-directory-helper tests', () => {
       git,
       repositoryPath,
       repositoryUrl,
-      clean
+      clean,
+      ref
     )
 
     // Assert
@@ -163,7 +169,8 @@ describe('git-directory-helper tests', () => {
       git,
       repositoryPath,
       differentRepositoryUrl,
-      clean
+      clean,
+      ref
     )
 
     // Assert
@@ -187,7 +194,8 @@ describe('git-directory-helper tests', () => {
       git,
       repositoryPath,
       repositoryUrl,
-      clean
+      clean,
+      ref
     )
 
     // Assert
@@ -212,7 +220,8 @@ describe('git-directory-helper tests', () => {
       git,
       repositoryPath,
       repositoryUrl,
-      clean
+      clean,
+      ref
     )
 
     // Assert
@@ -236,7 +245,8 @@ describe('git-directory-helper tests', () => {
       undefined,
       repositoryPath,
       repositoryUrl,
-      clean
+      clean,
+      ref
     )
 
     // Assert
@@ -260,7 +270,8 @@ describe('git-directory-helper tests', () => {
       git,
       repositoryPath,
       repositoryUrl,
-      clean
+      clean,
+      ref
     )
 
     // Assert
@@ -290,7 +301,8 @@ describe('git-directory-helper tests', () => {
       git,
       repositoryPath,
       repositoryUrl,
-      clean
+      clean,
+      ref
     )
 
     // Assert
@@ -305,29 +317,66 @@ describe('git-directory-helper tests', () => {
     expect(git.tryReset).not.toHaveBeenCalled()
   })
 
-  const removesRemoteBranches = 'removes local branches'
-  it(removesRemoteBranches, async () => {
+  const removesAncestorRemoteBranch = 'removes ancestor remote branch'
+  it(removesAncestorRemoteBranch, async () => {
     // Arrange
-    await setup(removesRemoteBranches)
+    await setup(removesAncestorRemoteBranch)
     await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
     const mockBranchList = git.branchList as jest.Mock<any, any>
     mockBranchList.mockImplementation(async (remote: boolean) => {
-      return remote ? ['remote-branch-1', 'remote-branch-2'] : []
+      return remote ? ['origin/remote-branch-1', 'origin/remote-branch-2'] : []
     })
+    ref = 'remote-branch-1/conflict'
 
     // Act
     await gitDirectoryHelper.prepareExistingDirectory(
       git,
       repositoryPath,
       repositoryUrl,
-      clean
+      clean,
+      ref
     )
 
     // Assert
     const files = await fs.promises.readdir(repositoryPath)
     expect(files.sort()).toEqual(['.git', 'my-file'])
-    expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-1')
-    expect(git.branchDelete).toHaveBeenCalledWith(true, 'remote-branch-2')
+    expect(git.branchDelete).toHaveBeenCalledTimes(1)
+    expect(git.branchDelete).toHaveBeenCalledWith(
+      true,
+      'origin/remote-branch-1'
+    )
+  })
+
+  const removesDescendantRemoteBranches = 'removes descendant remote branch'
+  it(removesDescendantRemoteBranches, async () => {
+    // Arrange
+    await setup(removesDescendantRemoteBranches)
+    await fs.promises.writeFile(path.join(repositoryPath, 'my-file'), '')
+    const mockBranchList = git.branchList as jest.Mock<any, any>
+    mockBranchList.mockImplementation(async (remote: boolean) => {
+      return remote
+        ? ['origin/remote-branch-1/conflict', 'origin/remote-branch-2']
+        : []
+    })
+    ref = 'remote-branch-1'
+
+    // Act
+    await gitDirectoryHelper.prepareExistingDirectory(
+      git,
+      repositoryPath,
+      repositoryUrl,
+      clean,
+      ref
+    )
+
+    // Assert
+    const files = await fs.promises.readdir(repositoryPath)
+    expect(files.sort()).toEqual(['.git', 'my-file'])
+    expect(git.branchDelete).toHaveBeenCalledTimes(1)
+    expect(git.branchDelete).toHaveBeenCalledWith(
+      true,
+      'origin/remote-branch-1/conflict'
+    )
   })
 })
 
@@ -344,6 +393,9 @@ async function setup(testName: string): Promise<void> {
   // Clean
   clean = true
 
+  // Ref
+  ref = ''
+
   // Git command manager
   git = {
     branchDelete: jest.fn(),
@@ -364,7 +416,9 @@ async function setup(testName: string): Promise<void> {
     log1: jest.fn(),
     remoteAdd: jest.fn(),
     removeEnvironmentVariable: jest.fn(),
+    revParse: jest.fn(),
     setEnvironmentVariable: jest.fn(),
+    shaExists: jest.fn(),
     submoduleForeach: jest.fn(),
     submoduleSync: jest.fn(),
     submoduleUpdate: jest.fn(),
diff --git a/adrs/0153-checkout-v2.md b/adrs/0153-checkout-v2.md
index b9536a5..f174b1a 100644
--- a/adrs/0153-checkout-v2.md
+++ b/adrs/0153-checkout-v2.md
@@ -70,7 +70,7 @@ We want to take this opportunity to make behavioral changes, from v1. This docum
     description: 'Whether to execute `git clean -ffdx && git reset --hard HEAD` before fetching'
     default: true
   fetch-depth:
-    description: 'Number of commits to fetch. 0 indicates all history.'
+    description: 'Number of commits to fetch. 0 indicates all history for all tags and branches.'
     default: 1
   lfs:
     description: 'Whether to download Git-LFS files'
diff --git a/dist/index.js b/dist/index.js
index 86601d1..0c78d25 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -3383,6 +3383,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
 const url_1 = __webpack_require__(835);
 const core = __importStar(__webpack_require__(470));
 const github = __importStar(__webpack_require__(469));
+exports.tagsRefSpec = '+refs/tags/*:refs/tags/*';
 function getCheckoutInfo(git, ref, commit) {
     return __awaiter(this, void 0, void 0, function* () {
         if (!git) {
@@ -3429,6 +3430,15 @@ function getCheckoutInfo(git, ref, commit) {
     });
 }
 exports.getCheckoutInfo = getCheckoutInfo;
+function getRefSpecForAllHistory(ref, commit) {
+    const result = ['+refs/heads/*:refs/remotes/origin/*', exports.tagsRefSpec];
+    if (ref && ref.toUpperCase().startsWith('REFS/PULL/')) {
+        const branch = ref.substring('refs/pull/'.length);
+        result.push(`+${commit || ref}:refs/remotes/pull/${branch}`);
+    }
+    return result;
+}
+exports.getRefSpecForAllHistory = getRefSpecForAllHistory;
 function getRefSpec(ref, commit) {
     if (!ref && !commit) {
         throw new Error('Args ref and commit cannot both be empty');
@@ -3478,6 +3488,50 @@ function getRefSpec(ref, commit) {
     }
 }
 exports.getRefSpec = getRefSpec;
+/**
+ * Tests whether the initial fetch created the ref at the expected commit
+ */
+function testRef(git, ref, commit) {
+    return __awaiter(this, void 0, void 0, function* () {
+        if (!git) {
+            throw new Error('Arg git cannot be empty');
+        }
+        if (!ref && !commit) {
+            throw new Error('Args ref and commit cannot both be empty');
+        }
+        // No SHA? Nothing to test
+        if (!commit) {
+            return true;
+        }
+        // SHA only?
+        else if (!ref) {
+            return yield git.shaExists(commit);
+        }
+        const upperRef = ref.toUpperCase();
+        // refs/heads/
+        if (upperRef.startsWith('REFS/HEADS/')) {
+            const branch = ref.substring('refs/heads/'.length);
+            return ((yield git.branchExists(true, `origin/${branch}`)) &&
+                commit === (yield git.revParse(`refs/remotes/origin/${branch}`)));
+        }
+        // refs/pull/
+        else if (upperRef.startsWith('REFS/PULL/')) {
+            // Assume matches because fetched using the commit
+            return true;
+        }
+        // refs/tags/
+        else if (upperRef.startsWith('REFS/TAGS/')) {
+            const tagName = ref.substring('refs/tags/'.length);
+            return ((yield git.tagExists(tagName)) && commit === (yield git.revParse(ref)));
+        }
+        // Unexpected
+        else {
+            core.debug(`Unexpected ref format '${ref}' when testing ref info`);
+            return true;
+        }
+    });
+}
+exports.testRef = testRef;
 function checkCommitInfo(token, commitInfo, repositoryOwner, repositoryName, ref, commit) {
     return __awaiter(this, void 0, void 0, function* () {
         try {
@@ -5634,6 +5688,7 @@ const exec = __importStar(__webpack_require__(986));
 const fshelper = __importStar(__webpack_require__(618));
 const io = __importStar(__webpack_require__(1));
 const path = __importStar(__webpack_require__(622));
+const refHelper = __importStar(__webpack_require__(227));
 const regexpHelper = __importStar(__webpack_require__(528));
 const retryHelper = __importStar(__webpack_require__(587));
 const git_version_1 = __webpack_require__(559);
@@ -5749,18 +5804,14 @@ class GitCommandManager {
             return output.exitCode === 0;
         });
     }
-    fetch(fetchDepth, refSpec) {
+    fetch(refSpec, fetchDepth) {
         return __awaiter(this, void 0, void 0, function* () {
-            const args = [
-                '-c',
-                'protocol.version=2',
-                'fetch',
-                '--no-tags',
-                '--prune',
-                '--progress',
-                '--no-recurse-submodules'
-            ];
-            if (fetchDepth > 0) {
+            const args = ['-c', 'protocol.version=2', 'fetch'];
+            if (!refSpec.some(x => x === refHelper.tagsRefSpec)) {
+                args.push('--no-tags');
+            }
+            args.push('--prune', '--progress', '--no-recurse-submodules');
+            if (fetchDepth && fetchDepth > 0) {
                 args.push(`--depth=${fetchDepth}`);
             }
             else if (fshelper.fileExistsSync(path.join(this.workingDirectory, '.git', 'shallow'))) {
@@ -5819,9 +5870,28 @@ class GitCommandManager {
     removeEnvironmentVariable(name) {
         delete this.gitEnv[name];
     }
+    /**
+     * Resolves a ref to a SHA. For a branch or lightweight tag, the commit SHA is returned.
+     * For an annotated tag, the tag SHA is returned.
+     * @param {string} ref  For example: 'refs/heads/master' or '/refs/tags/v1'
+     * @returns {Promise<string>}
+     */
+    revParse(ref) {
+        return __awaiter(this, void 0, void 0, function* () {
+            const output = yield this.execGit(['rev-parse', ref]);
+            return output.stdout.trim();
+        });
+    }
     setEnvironmentVariable(name, value) {
         this.gitEnv[name] = value;
     }
+    shaExists(sha) {
+        return __awaiter(this, void 0, void 0, function* () {
+            const args = ['rev-parse', '--verify', '--quiet', `${sha}^{object}`];
+            const output = yield this.execGit(args, true);
+            return output.exitCode === 0;
+        });
+    }
     submoduleForeach(command, recursive) {
         return __awaiter(this, void 0, void 0, function* () {
             const args = ['submodule', 'foreach'];
@@ -6060,7 +6130,7 @@ function getSource(settings) {
         core.endGroup();
         // Prepare existing directory, otherwise recreate
         if (isExisting) {
-            yield gitDirectoryHelper.prepareExistingDirectory(git, settings.repositoryPath, repositoryUrl, settings.clean);
+            yield gitDirectoryHelper.prepareExistingDirectory(git, settings.repositoryPath, repositoryUrl, settings.clean, settings.ref);
         }
         if (!git) {
             // Downloading using REST API
@@ -6102,8 +6172,21 @@ function getSource(settings) {
             }
             // Fetch
             core.startGroup('Fetching the repository');
-            const refSpec = refHelper.getRefSpec(settings.ref, settings.commit);
-            yield git.fetch(settings.fetchDepth, refSpec);
+            if (settings.fetchDepth <= 0) {
+                // Fetch all branches and tags
+                let refSpec = refHelper.getRefSpecForAllHistory(settings.ref, settings.commit);
+                yield git.fetch(refSpec);
+                // When all history is fetched, the ref we're interested in may have moved to a different
+                // commit (push or force push). If so, fetch again with a targeted refspec.
+                if (!(yield refHelper.testRef(git, settings.ref, settings.commit))) {
+                    refSpec = refHelper.getRefSpec(settings.ref, settings.commit);
+                    yield git.fetch(refSpec);
+                }
+            }
+            else {
+                const refSpec = refHelper.getRefSpec(settings.ref, settings.commit);
+                yield git.fetch(refSpec, settings.fetchDepth);
+            }
             core.endGroup();
             // Checkout info
             core.startGroup('Determining the checkout info');
@@ -7454,7 +7537,7 @@ const fs = __importStar(__webpack_require__(747));
 const fsHelper = __importStar(__webpack_require__(618));
 const io = __importStar(__webpack_require__(1));
 const path = __importStar(__webpack_require__(622));
-function prepareExistingDirectory(git, repositoryPath, repositoryUrl, clean) {
+function prepareExistingDirectory(git, repositoryPath, repositoryUrl, clean, ref) {
     return __awaiter(this, void 0, void 0, function* () {
         assert.ok(repositoryPath, 'Expected repositoryPath to be defined');
         assert.ok(repositoryUrl, 'Expected repositoryUrl to be defined');
@@ -7494,10 +7577,24 @@ function prepareExistingDirectory(git, repositoryPath, repositoryUrl, clean) {
                 for (const branch of branches) {
                     yield git.branchDelete(false, branch);
                 }
-                // Remove all refs/remotes/origin/* to avoid conflicts
-                branches = yield git.branchList(true);
-                for (const branch of branches) {
-                    yield git.branchDelete(true, branch);
+                // Remove any conflicting refs/remotes/origin/*
+                // Example 1: Consider ref is refs/heads/foo and previously fetched refs/remotes/origin/foo/bar
+                // Example 2: Consider ref is refs/heads/foo/bar and previously fetched refs/remotes/origin/foo
+                if (ref) {
+                    ref = ref.startsWith('refs/') ? ref : `refs/heads/${ref}`;
+                    if (ref.startsWith('refs/heads/')) {
+                        const upperName1 = ref.toUpperCase().substr('REFS/HEADS/'.length);
+                        const upperName1Slash = `${upperName1}/`;
+                        branches = yield git.branchList(true);
+                        for (const branch of branches) {
+                            const upperName2 = branch.substr('origin/'.length).toUpperCase();
+                            const upperName2Slash = `${upperName2}/`;
+                            if (upperName1.startsWith(upperName2Slash) ||
+                                upperName2.startsWith(upperName1Slash)) {
+                                yield git.branchDelete(true, branch);
+                            }
+                        }
+                    }
                 }
                 core.endGroup();
                 // Clean
diff --git a/src/git-command-manager.ts b/src/git-command-manager.ts
index 495075a..9d2d45f 100644
--- a/src/git-command-manager.ts
+++ b/src/git-command-manager.ts
@@ -3,6 +3,7 @@ import * as exec from '@actions/exec'
 import * as fshelper from './fs-helper'
 import * as io from '@actions/io'
 import * as path from 'path'
+import * as refHelper from './ref-helper'
 import * as regexpHelper from './regexp-helper'
 import * as retryHelper from './retry-helper'
 import {GitVersion} from './git-version'
@@ -23,7 +24,7 @@ export interface IGitCommandManager {
     globalConfig?: boolean
   ): Promise<void>
   configExists(configKey: string, globalConfig?: boolean): Promise<boolean>
-  fetch(fetchDepth: number, refSpec: string[]): Promise<void>
+  fetch(refSpec: string[], fetchDepth?: number): Promise<void>
   getWorkingDirectory(): string
   init(): Promise<void>
   isDetached(): Promise<boolean>
@@ -32,7 +33,9 @@ export interface IGitCommandManager {
   log1(): Promise<string>
   remoteAdd(remoteName: string, remoteUrl: string): Promise<void>
   removeEnvironmentVariable(name: string): void
+  revParse(ref: string): Promise<string>
   setEnvironmentVariable(name: string, value: string): void
+  shaExists(sha: string): Promise<boolean>
   submoduleForeach(command: string, recursive: boolean): Promise<string>
   submoduleSync(recursive: boolean): Promise<void>
   submoduleUpdate(fetchDepth: number, recursive: boolean): Promise<void>
@@ -164,17 +167,14 @@ class GitCommandManager {
     return output.exitCode === 0
   }
 
-  async fetch(fetchDepth: number, refSpec: string[]): Promise<void> {
-    const args = [
-      '-c',
-      'protocol.version=2',
-      'fetch',
-      '--no-tags',
-      '--prune',
-      '--progress',
-      '--no-recurse-submodules'
-    ]
-    if (fetchDepth > 0) {
+  async fetch(refSpec: string[], fetchDepth?: number): Promise<void> {
+    const args = ['-c', 'protocol.version=2', 'fetch']
+    if (!refSpec.some(x => x === refHelper.tagsRefSpec)) {
+      args.push('--no-tags')
+    }
+
+    args.push('--prune', '--progress', '--no-recurse-submodules')
+    if (fetchDepth && fetchDepth > 0) {
       args.push(`--depth=${fetchDepth}`)
     } else if (
       fshelper.fileExistsSync(
@@ -238,10 +238,27 @@ class GitCommandManager {
     delete this.gitEnv[name]
   }
 
+  /**
+   * Resolves a ref to a SHA. For a branch or lightweight tag, the commit SHA is returned.
+   * For an annotated tag, the tag SHA is returned.
+   * @param {string} ref  For example: 'refs/heads/master' or '/refs/tags/v1'
+   * @returns {Promise<string>}
+   */
+  async revParse(ref: string): Promise<string> {
+    const output = await this.execGit(['rev-parse', ref])
+    return output.stdout.trim()
+  }
+
   setEnvironmentVariable(name: string, value: string): void {
     this.gitEnv[name] = value
   }
 
+  async shaExists(sha: string): Promise<boolean> {
+    const args = ['rev-parse', '--verify', '--quiet', `${sha}^{object}`]
+    const output = await this.execGit(args, true)
+    return output.exitCode === 0
+  }
+
   async submoduleForeach(command: string, recursive: boolean): Promise<string> {
     const args = ['submodule', 'foreach']
     if (recursive) {
diff --git a/src/git-directory-helper.ts b/src/git-directory-helper.ts
index 3866866..e792190 100644
--- a/src/git-directory-helper.ts
+++ b/src/git-directory-helper.ts
@@ -5,13 +5,13 @@ import * as fsHelper from './fs-helper'
 import * as io from '@actions/io'
 import * as path from 'path'
 import {IGitCommandManager} from './git-command-manager'
-import {IGitSourceSettings} from './git-source-settings'
 
 export async function prepareExistingDirectory(
   git: IGitCommandManager | undefined,
   repositoryPath: string,
   repositoryUrl: string,
-  clean: boolean
+  clean: boolean,
+  ref: string
 ): Promise<void> {
   assert.ok(repositoryPath, 'Expected repositoryPath to be defined')
   assert.ok(repositoryUrl, 'Expected repositoryUrl to be defined')
@@ -56,10 +56,26 @@ export async function prepareExistingDirectory(
         await git.branchDelete(false, branch)
       }
 
-      // Remove all refs/remotes/origin/* to avoid conflicts
-      branches = await git.branchList(true)
-      for (const branch of branches) {
-        await git.branchDelete(true, branch)
+      // Remove any conflicting refs/remotes/origin/*
+      // Example 1: Consider ref is refs/heads/foo and previously fetched refs/remotes/origin/foo/bar
+      // Example 2: Consider ref is refs/heads/foo/bar and previously fetched refs/remotes/origin/foo
+      if (ref) {
+        ref = ref.startsWith('refs/') ? ref : `refs/heads/${ref}`
+        if (ref.startsWith('refs/heads/')) {
+          const upperName1 = ref.toUpperCase().substr('REFS/HEADS/'.length)
+          const upperName1Slash = `${upperName1}/`
+          branches = await git.branchList(true)
+          for (const branch of branches) {
+            const upperName2 = branch.substr('origin/'.length).toUpperCase()
+            const upperName2Slash = `${upperName2}/`
+            if (
+              upperName1.startsWith(upperName2Slash) ||
+              upperName2.startsWith(upperName1Slash)
+            ) {
+              await git.branchDelete(true, branch)
+            }
+          }
+        }
       }
       core.endGroup()
 
diff --git a/src/git-source-provider.ts b/src/git-source-provider.ts
index bde53bb..89c16b5 100644
--- a/src/git-source-provider.ts
+++ b/src/git-source-provider.ts
@@ -42,7 +42,8 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
       git,
       settings.repositoryPath,
       repositoryUrl,
-      settings.clean
+      settings.clean,
+      settings.ref
     )
   }
 
@@ -109,8 +110,24 @@ export async function getSource(settings: IGitSourceSettings): Promise<void> {
 
     // Fetch
     core.startGroup('Fetching the repository')
-    const refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
-    await git.fetch(settings.fetchDepth, refSpec)
+    if (settings.fetchDepth <= 0) {
+      // Fetch all branches and tags
+      let refSpec = refHelper.getRefSpecForAllHistory(
+        settings.ref,
+        settings.commit
+      )
+      await git.fetch(refSpec)
+
+      // When all history is fetched, the ref we're interested in may have moved to a different
+      // commit (push or force push). If so, fetch again with a targeted refspec.
+      if (!(await refHelper.testRef(git, settings.ref, settings.commit))) {
+        refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
+        await git.fetch(refSpec)
+      }
+    } else {
+      const refSpec = refHelper.getRefSpec(settings.ref, settings.commit)
+      await git.fetch(refSpec, settings.fetchDepth)
+    }
     core.endGroup()
 
     // Checkout info
diff --git a/src/ref-helper.ts b/src/ref-helper.ts
index f600179..381fa60 100644
--- a/src/ref-helper.ts
+++ b/src/ref-helper.ts
@@ -3,6 +3,8 @@ import {IGitCommandManager} from './git-command-manager'
 import * as core from '@actions/core'
 import * as github from '@actions/github'
 
+export const tagsRefSpec = '+refs/tags/*:refs/tags/*'
+
 export interface ICheckoutInfo {
   ref: string
   startPoint: string
@@ -60,6 +62,16 @@ export async function getCheckoutInfo(
   return result
 }
 
+export function getRefSpecForAllHistory(ref: string, commit: string): string[] {
+  const result = ['+refs/heads/*:refs/remotes/origin/*', tagsRefSpec]
+  if (ref && ref.toUpperCase().startsWith('REFS/PULL/')) {
+    const branch = ref.substring('refs/pull/'.length)
+    result.push(`+${commit || ref}:refs/remotes/pull/${branch}`)
+  }
+
+  return result
+}
+
 export function getRefSpec(ref: string, commit: string): string[] {
   if (!ref && !commit) {
     throw new Error('Args ref and commit cannot both be empty')
@@ -111,6 +123,60 @@ export function getRefSpec(ref: string, commit: string): string[] {
   }
 }
 
+/**
+ * Tests whether the initial fetch created the ref at the expected commit
+ */
+export async function testRef(
+  git: IGitCommandManager,
+  ref: string,
+  commit: string
+): Promise<boolean> {
+  if (!git) {
+    throw new Error('Arg git cannot be empty')
+  }
+
+  if (!ref && !commit) {
+    throw new Error('Args ref and commit cannot both be empty')
+  }
+
+  // No SHA? Nothing to test
+  if (!commit) {
+    return true
+  }
+  // SHA only?
+  else if (!ref) {
+    return await git.shaExists(commit)
+  }
+
+  const upperRef = ref.toUpperCase()
+
+  // refs/heads/
+  if (upperRef.startsWith('REFS/HEADS/')) {
+    const branch = ref.substring('refs/heads/'.length)
+    return (
+      (await git.branchExists(true, `origin/${branch}`)) &&
+      commit === (await git.revParse(`refs/remotes/origin/${branch}`))
+    )
+  }
+  // refs/pull/
+  else if (upperRef.startsWith('REFS/PULL/')) {
+    // Assume matches because fetched using the commit
+    return true
+  }
+  // refs/tags/
+  else if (upperRef.startsWith('REFS/TAGS/')) {
+    const tagName = ref.substring('refs/tags/'.length)
+    return (
+      (await git.tagExists(tagName)) && commit === (await git.revParse(ref))
+    )
+  }
+  // Unexpected
+  else {
+    core.debug(`Unexpected ref format '${ref}' when testing ref info`)
+    return true
+  }
+}
+
 export async function checkCommitInfo(
   token: string,
   commitInfo: string,