@@ -11,7 +11,7 @@ import TelemetryReporter from '@vscode/extension-telemetry';
11
11
import { Branch , Change , ForcePushMode , GitErrorCodes , LogOptions , Ref , RefType , Remote , Status , CommitOptions , BranchQuery , FetchOptions } from './api/git' ;
12
12
import { AutoFetcher } from './autofetch' ;
13
13
import { debounce , memoize , throttle } from './decorators' ;
14
- import { Commit , GitError , Repository as BaseRepository , Stash , Submodule , LogFileOptions } from './git' ;
14
+ import { Commit , GitError , Repository as BaseRepository , Stash , Submodule , LogFileOptions , PullOptions } from './git' ;
15
15
import { StatusBarCommands } from './statusbar' ;
16
16
import { toGitUri } from './uri' ;
17
17
import { anyEvent , combinedDisposable , debounceEvent , dispose , EmptyDisposable , eventToPromise , filterEvent , find , IDisposable , isDescendant , onceEvent , pathEquals , relativePath } from './util' ;
@@ -1658,12 +1658,28 @@ export class Repository implements Disposable {
1658
1658
}
1659
1659
1660
1660
if ( await this . checkIfMaybeRebased ( this . HEAD ?. name ) ) {
1661
- await this . repository . pull ( rebase , remote , branch , { unshallow, tags } ) ;
1661
+ this . _pullAndHandleTagConflict ( rebase , remote , branch , { unshallow, tags } ) ;
1662
1662
}
1663
1663
} ) ;
1664
1664
} ) ;
1665
1665
}
1666
1666
1667
+ private async _pullAndHandleTagConflict ( rebase ?: boolean , remote ?: string , branch ?: string , options : PullOptions = { } ) : Promise < void > {
1668
+ try {
1669
+ await this . repository . pull ( rebase , remote , branch , options ) ;
1670
+ }
1671
+ catch ( err ) {
1672
+ if ( err . gitErrorCode !== GitErrorCodes . TagConflict ) {
1673
+ throw err ;
1674
+ }
1675
+
1676
+ // Handle tag(s) conflict
1677
+ if ( await this . handleTagConflict ( remote , err . stderr ) ) {
1678
+ await this . repository . pull ( rebase , remote , branch , options ) ;
1679
+ }
1680
+ }
1681
+ }
1682
+
1667
1683
@throttle
1668
1684
async push ( head : Branch , forcePushMode ?: ForcePushMode ) : Promise < void > {
1669
1685
let remote : string | undefined ;
@@ -1724,7 +1740,7 @@ export class Repository implements Disposable {
1724
1740
}
1725
1741
1726
1742
if ( await this . checkIfMaybeRebased ( this . HEAD ?. name ) ) {
1727
- await this . repository . pull ( rebase , remoteName , pullBranch , { tags, cancellationToken } ) ;
1743
+ this . _pullAndHandleTagConflict ( rebase , remoteName , pullBranch , { tags, cancellationToken } ) ;
1728
1744
}
1729
1745
} ;
1730
1746
@@ -2476,6 +2492,38 @@ export class Repository implements Disposable {
2476
2492
return config . get < boolean > ( 'optimisticUpdate' ) === true ;
2477
2493
}
2478
2494
2495
+ private async handleTagConflict ( remote : string | undefined , raw : string ) : Promise < boolean > {
2496
+ // Ensure there is a remote
2497
+ remote = remote ?? this . HEAD ?. upstream ?. remote ;
2498
+ if ( ! remote ) {
2499
+ throw new Error ( 'Unable to resolve tag conflict due to missing remote.' ) ;
2500
+ }
2501
+
2502
+ // Extract tag names from message
2503
+ const tags : string [ ] = [ ] ;
2504
+ for ( const match of raw . matchAll ( / ^ ! \[ r e j e c t e d \] \s + ( [ ^ \s ] + ) \s + - > \s + ( [ ^ \s ] + ) \s + \( w o u l d c l o b b e r e x i s t i n g t a g \) $ / gm) ) {
2505
+ if ( match . length === 3 ) {
2506
+ tags . push ( match [ 1 ] ) ;
2507
+ }
2508
+ }
2509
+ if ( tags . length === 0 ) {
2510
+ throw new Error ( `Unable to extract tag names from error message: ${ raw } ` ) ;
2511
+ }
2512
+
2513
+ // Notification
2514
+ const replaceLocalTags = l10n . t ( 'Replace Local Tag(s)' ) ;
2515
+ const message = l10n . t ( 'Unable to pull from remote repository due to conflicting tag(s): {0}. Would you like to resolve the conflict by replacing the local tag(s)?' , tags . join ( ', ' ) ) ;
2516
+ const choice = await window . showErrorMessage ( message , { modal : true } , replaceLocalTags ) ;
2517
+
2518
+ if ( choice !== replaceLocalTags ) {
2519
+ return false ;
2520
+ }
2521
+
2522
+ // Force fetch tags
2523
+ await this . repository . fetchTags ( { remote, tags, force : true } ) ;
2524
+ return true ;
2525
+ }
2526
+
2479
2527
public isBranchProtected ( name : string = this . HEAD ?. name ?? '' ) : boolean {
2480
2528
return this . isBranchProtectedMatcher ? this . isBranchProtectedMatcher ( name ) : false ;
2481
2529
}
0 commit comments