From cbf7d34e011b662fd3370c90b9a91d42c4798b00 Mon Sep 17 00:00:00 2001 From: NikeKov Date: Thu, 30 Jul 2026 23:23:34 +0300 Subject: [PATCH 1/2] Add "Push Current Commit Tags" action to Remotes menu Adds a Remotes menu item (Cmd+Shift+Ctrl+T) that pushes the tags pointing at the current commit to all remotes. The current commit is the commit selected in the graph, or HEAD when nothing is selected. Tags are force-pushed, consistent with the existing tag push behavior in GitUp. The menu item is disabled when the current commit has no tags. --- GitUp/Application/Base.lproj/MainMenu.xib | 6 +++ .../Views/GIMapViewController+Operations.h | 1 + .../Views/GIMapViewController+Operations.m | 46 +++++++++++++++++++ GitUpKit/Views/GIMapViewController.h | 1 + GitUpKit/Views/GIMapViewController.m | 9 ++++ 5 files changed, 63 insertions(+) diff --git a/GitUp/Application/Base.lproj/MainMenu.xib b/GitUp/Application/Base.lproj/MainMenu.xib index 528efb476..3630edc0c 100644 --- a/GitUp/Application/Base.lproj/MainMenu.xib +++ b/GitUp/Application/Base.lproj/MainMenu.xib @@ -292,6 +292,12 @@ + + + + + + diff --git a/GitUpKit/Views/GIMapViewController+Operations.h b/GitUpKit/Views/GIMapViewController+Operations.h index 1b6df0c9a..337f60609 100644 --- a/GitUpKit/Views/GIMapViewController+Operations.h +++ b/GitUpKit/Views/GIMapViewController+Operations.h @@ -59,6 +59,7 @@ - (void)pushAllLocalBranchesToAllRemotes; - (void)pushTag:(GCHistoryTag*)tag toRemote:(GCRemote*)remote; - (void)pushAllTagsToAllRemotes; +- (void)pushTagsToAllRemotes:(NSArray*)tags; - (void)pullLocalBranchFromUpstream:(GCHistoryLocalBranch*)branch; @end diff --git a/GitUpKit/Views/GIMapViewController+Operations.m b/GitUpKit/Views/GIMapViewController+Operations.m index 3af3f45f0..c9fea9830 100644 --- a/GitUpKit/Views/GIMapViewController+Operations.m +++ b/GitUpKit/Views/GIMapViewController+Operations.m @@ -971,6 +971,52 @@ - (void)pushAllTagsToAllRemotes { } } +// IMPORTANT: See comment above regarding tags being force-pushed +- (void)pushTagsToAllRemotes:(NSArray*)tags { + if (!tags.count) { + return; + } + + NSString* names = [[tags valueForKey:@"name"] componentsJoinedByString:@", "]; + [self confirmUserActionWithAlertType:kGIAlertType_Caution + title:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to push the current commit tags (%@) to all remotes?", nil), names] + message:NSLocalizedString(@"This action cannot be undone.", nil) + button:NSLocalizedString(@"Push Tags", nil) + suppressionUserDefaultKey:kUserDefaultsKey_SkipPushTagWarning + block:^{ + NSError* localError; + NSArray* remotes = [self.repository listRemotes:&localError]; + if (remotes == nil) { + [self presentError:localError]; + return; + } + if (!remotes.count) { + [self.windowController showOverlayWithStyle:kGIOverlayStyle_Warning message:NSLocalizedString(@"There are no remotes to push to!", nil)]; + return; + } + + [self.repository performOperationInBackgroundWithReason:nil + argument:nil + usingOperationBlock:^BOOL(GCRepository* repository, NSError** error) { + for (GCRemote* remote in remotes) { + for (GCTag* tag in tags) { + if (![repository pushTag:tag toRemote:remote force:YES error:error]) { + return NO; + } + } + } + return YES; + } + completionBlock:^(BOOL success, NSError* error) { + if (success) { + [self.windowController showOverlayWithStyle:kGIOverlayStyle_Informational message:NSLocalizedString(@"The current commit tags were pushed to all remotes successfully!", nil)]; + } else { + [self presentError:error]; + } + }]; + }]; +} + #pragma mark - Remote Delete - (void)_deleteRemoteBranchFromRemote:(GCHistoryRemoteBranch*)branch { diff --git a/GitUpKit/Views/GIMapViewController.h b/GitUpKit/Views/GIMapViewController.h index 589fda740..df767e4d9 100644 --- a/GitUpKit/Views/GIMapViewController.h +++ b/GitUpKit/Views/GIMapViewController.h @@ -55,4 +55,5 @@ - (IBAction)pushAllTags:(id)sender; - (IBAction)pullCurrentBranch:(id)sender; - (IBAction)pushCurrentBranch:(id)sender; +- (IBAction)pushCurrentCommitTags:(id)sender; @end diff --git a/GitUpKit/Views/GIMapViewController.m b/GitUpKit/Views/GIMapViewController.m index 69efaed93..dbfdf7162 100644 --- a/GitUpKit/Views/GIMapViewController.m +++ b/GitUpKit/Views/GIMapViewController.m @@ -638,6 +638,10 @@ - (BOOL)validateUserInterfaceItem:(id)item { if (item.action == @selector(pushCurrentBranch:)) { return !editingDisabled && self.repository.history.HEADBranch; } + if (item.action == @selector(pushCurrentCommitTags:)) { + GCHistoryCommit* currentCommit = _graphView.selectedCommit ?: self.repository.history.HEADCommit; + return !editingDisabled && currentCommit.tags.count > 0; + } GCHistoryCommit* commit = _graphView.selectedCommit; if (commit == nil) { @@ -793,6 +797,11 @@ - (IBAction)pushCurrentBranch:(id)sender { } } +- (IBAction)pushCurrentCommitTags:(id)sender { + GCHistoryCommit* commit = _graphView.selectedCommit ?: self.repository.history.HEADCommit; + [self pushTagsToAllRemotes:commit.tags]; +} + #pragma mark - Contextual Menu Actions - (IBAction)quickViewSelectedCommit:(id)sender { From 4248a810e4e48b713522ea3f0a39be02f0c1a900 Mon Sep 17 00:00:00 2001 From: NikeKov Date: Thu, 30 Jul 2026 23:37:31 +0300 Subject: [PATCH 2/2] Use a dedicated suppression key for pushing current commit tags Pushing the current commit tags force-pushes to every remote, which has a larger blast radius than the single-tag-to-single-remote push that owns kUserDefaultsKey_SkipPushTagWarning. Sharing the key meant suppressing the narrower dialog would silently skip confirmation for the broader action. --- GitUpKit/Views/GIMapViewController+Operations.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GitUpKit/Views/GIMapViewController+Operations.m b/GitUpKit/Views/GIMapViewController+Operations.m index c9fea9830..95f10733b 100644 --- a/GitUpKit/Views/GIMapViewController+Operations.m +++ b/GitUpKit/Views/GIMapViewController+Operations.m @@ -26,6 +26,7 @@ #define kUserDefaultsPrefix @"GIMapViewController_" #define kUserDefaultsKey_SkipPushTagWarning kUserDefaultsPrefix "SkipPushTagWarning" +#define kUserDefaultsKey_SkipPushCurrentCommitTagsWarning kUserDefaultsPrefix "SkipPushCurrentCommitTagsWarning" #define kUserDefaultsKey_SkipFetchRemoteBranchWarning kUserDefaultsPrefix "SkipFetchRemoteBranchWarning" #define kUserDefaultsKey_SkipPullBranchWarning kUserDefaultsPrefix "SkipPullBranchWarning" #define kUserDefaultsKey_SkipPushBranchWarning kUserDefaultsPrefix "SkipPushBranchWarning" @@ -982,7 +983,7 @@ - (void)pushTagsToAllRemotes:(NSArray*)tags { title:[NSString stringWithFormat:NSLocalizedString(@"Are you sure you want to push the current commit tags (%@) to all remotes?", nil), names] message:NSLocalizedString(@"This action cannot be undone.", nil) button:NSLocalizedString(@"Push Tags", nil) - suppressionUserDefaultKey:kUserDefaultsKey_SkipPushTagWarning + suppressionUserDefaultKey:kUserDefaultsKey_SkipPushCurrentCommitTagsWarning block:^{ NSError* localError; NSArray* remotes = [self.repository listRemotes:&localError];