diff --git a/CodeEdit.xcodeproj/project.pbxproj b/CodeEdit.xcodeproj/project.pbxproj
index 9a1315605..27cd44f88 100644
--- a/CodeEdit.xcodeproj/project.pbxproj
+++ b/CodeEdit.xcodeproj/project.pbxproj
@@ -380,6 +380,9 @@
 		85773E1E2A3E0A1F00C5D926 /* SettingsSearchResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85773E1D2A3E0A1F00C5D926 /* SettingsSearchResult.swift */; };
 		85CD0C5F2A10CC3200E531FD /* URL+isImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CD0C5E2A10CC3200E531FD /* URL+isImage.swift */; };
 		85E4122A2A46C8CA00183F2B /* LocationsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85E412292A46C8CA00183F2B /* LocationsSettings.swift */; };
+		8B46FD062B732DAA00573116 /* GitTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B46FD052B732DAA00573116 /* GitTag.swift */; };
+		8B46FD0A2B7521E300573116 /* SourceControlNavigatorAddTagView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B46FD092B7521E300573116 /* SourceControlNavigatorAddTagView.swift */; };
+		8E6377942B72C48D00E2403F /* GitClient+Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E6377932B72C48D00E2403F /* GitClient+Tag.swift */; };
 		9D36E1BF2B5E7D7500443C41 /* GitBranchesGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D36E1BE2B5E7D7500443C41 /* GitBranchesGroup.swift */; };
 		B6041F4D29D7A4E9000F3454 /* SettingsPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6041F4C29D7A4E9000F3454 /* SettingsPageView.swift */; };
 		B6041F5229D7D6D6000F3454 /* SettingsWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6041F5129D7D6D6000F3454 /* SettingsWindow.swift */; };
@@ -924,6 +927,9 @@
 		85773E1D2A3E0A1F00C5D926 /* SettingsSearchResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSearchResult.swift; sourceTree = "<group>"; };
 		85CD0C5E2A10CC3200E531FD /* URL+isImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URL+isImage.swift"; sourceTree = "<group>"; };
 		85E412292A46C8CA00183F2B /* LocationsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationsSettings.swift; sourceTree = "<group>"; };
+		8B46FD052B732DAA00573116 /* GitTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitTag.swift; sourceTree = "<group>"; };
+		8B46FD092B7521E300573116 /* SourceControlNavigatorAddTagView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceControlNavigatorAddTagView.swift; sourceTree = "<group>"; };
+		8E6377932B72C48D00E2403F /* GitClient+Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GitClient+Tag.swift"; sourceTree = "<group>"; };
 		9D36E1BE2B5E7D7500443C41 /* GitBranchesGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitBranchesGroup.swift; sourceTree = "<group>"; };
 		B6041F4C29D7A4E9000F3454 /* SettingsPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPageView.swift; sourceTree = "<group>"; };
 		B6041F5129D7D6D6000F3454 /* SettingsWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsWindow.swift; sourceTree = "<group>"; };
@@ -2015,6 +2021,7 @@
 				B65B11032B09DB1C002852CF /* GitClient+Fetch.swift */,
 				B607181C2B0C5BE3009CDAB4 /* GitClient+Stash.swift */,
 				B628B7922B18369800F9775A /* GitClient+Validate.swift */,
+				8E6377932B72C48D00E2403F /* GitClient+Tag.swift */,
 			);
 			path = Client;
 			sourceTree = "<group>";
@@ -2023,6 +2030,7 @@
 			isa = PBXGroup;
 			children = (
 				587B9E5329301D8F00AC7927 /* GitCommit.swift */,
+				8B46FD052B732DAA00573116 /* GitTag.swift */,
 				587B9E5429301D8F00AC7927 /* GitChangedFile.swift */,
 				587B9E5529301D8F00AC7927 /* GitType.swift */,
 				04BA7C0A2AE2A2D100584E1C /* GitBranch.swift */,
@@ -2677,6 +2685,7 @@
 			children = (
 				B6C4F2A02B3CA37500B2B140 /* SourceControlNavigatorHistoryView.swift */,
 				20EBB504280C329800F3A5DA /* CommitListItemView.swift */,
+				8B46FD092B7521E300573116 /* SourceControlNavigatorAddTagView.swift */,
 				B6C4F2A22B3CA74800B2B140 /* CommitDetailsView.swift */,
 				B6C4F2A82B3CB00100B2B140 /* CommitDetailsHeaderView.swift */,
 				B6C4F2AB2B3CC4D000B2B140 /* CommitChangedFileListItemView.swift */,
@@ -3354,6 +3363,7 @@
 				6CFF967429BEBCC300182D6F /* FindCommands.swift in Sources */,
 				587B9E6529301D8F00AC7927 /* GitLabGroupAccess.swift in Sources */,
 				6C91D57229B176FF0059A90D /* EditorManager.swift in Sources */,
+				8B46FD062B732DAA00573116 /* GitTag.swift in Sources */,
 				6C82D6BC29C00CD900495C54 /* FirstResponderPropertyWrapper.swift in Sources */,
 				58D01C9B293167DC00C5B6B4 /* CodeEditKeychainConstants.swift in Sources */,
 				B640A99E29E2184700715F20 /* SettingsForm.swift in Sources */,
@@ -3387,6 +3397,7 @@
 				6C7F37FE2A3EA6FA00217B83 /* View+focusedValue.swift in Sources */,
 				B6C4F2A12B3CA37500B2B140 /* SourceControlNavigatorHistoryView.swift in Sources */,
 				B6C6A43029771F7100A3D28F /* EditorTabBackground.swift in Sources */,
+				8E6377942B72C48D00E2403F /* GitClient+Tag.swift in Sources */,
 				B60718372B170638009CDAB4 /* SourceControlNavigatorRenameBranchView.swift in Sources */,
 				6C578D8129CD294800DC73B2 /* ExtensionActivatorView.swift in Sources */,
 				B6F0517D29D9E4B100D72287 /* TerminalSettingsView.swift in Sources */,
@@ -3554,6 +3565,7 @@
 				6CAAF68A29BC9C2300A1F48A /* (null) in Sources */,
 				6C6BD6EF29CD12E900235D17 /* ExtensionManagerWindow.swift in Sources */,
 				6CFF967629BEBCD900182D6F /* FileCommands.swift in Sources */,
+				8B46FD0A2B7521E300573116 /* SourceControlNavigatorAddTagView.swift in Sources */,
 				B60718462B17DC15009CDAB4 /* RepoOutlineGroupItem.swift in Sources */,
 				613899B32B6E6FEE00A5CAF6 /* FuzzySearchable.swift in Sources */,
 				B697937A29FF5668002027EC /* AccountsSettingsAccountLink.swift in Sources */,
diff --git a/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager.swift b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager.swift
index 09ed8554a..c4f37d9b6 100644
--- a/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager.swift
+++ b/CodeEdit/Features/CEWorkspace/Models/CEWorkspaceFileManager.swift
@@ -264,6 +264,14 @@ final class CEWorkspaceFileManager {
             $0.path == "\(self.folderUrl.relativePath)/.git/config"
         })
 
+        let gitTagsChange = events.first(where: {
+            $0.path == "\(self.folderUrl.relativePath)/.git/packed-refs"
+        })
+
+        let gitTagsChange2 = events.first(where: {
+            $0.path.contains("\(self.folderUrl.relativePath)/.git/refs/tags")
+        })
+
         // If changes were made to project OR files were staged, refresh changes
         if !notGitChanges.isEmpty || gitIndexChange != nil {
             Task {
@@ -305,6 +313,12 @@ final class CEWorkspaceFileManager {
                 try await self.sourceControlManager?.validate()
             }
         }
+        
+        if gitTagsChange != nil || gitTagsChange2 != nil {
+            Task {
+                try await self.sourceControlManager?.refreshTags()
+            }
+        }
     }
 
     /// Creates or deletes children of the ``CEWorkspaceFile`` so that they are accurate with the file system,
diff --git a/CodeEdit/Features/Git/Client/GitClient+Tag.swift b/CodeEdit/Features/Git/Client/GitClient+Tag.swift
new file mode 100644
index 000000000..abe3e920e
--- /dev/null
+++ b/CodeEdit/Features/Git/Client/GitClient+Tag.swift
@@ -0,0 +1,59 @@
+//
+//  GitClient+Tag.swift
+//  CodeEdit
+//
+//  Created by Johnathan Baird on 2/6/24.
+//
+
+import Foundation
+
+extension GitClient {
+    /// Determines if the current directory is a valid git repository.
+    ///
+    /// Runs `git rev-parse --is-inside-work-tree`.
+    ///
+    /// - Returns: True, if git finds a valid repository.
+    func createTag(tagName: String, commitHash: String, message: String?) async -> Bool {
+        do {
+            let output = try await run("tag -a \(tagName) \(commitHash) -m \(message ?? "")")
+            return output.trimmingCharacters(in: .whitespacesAndNewlines) == "true"
+        } catch {
+            return false
+        }
+    }
+
+    func getTags() async throws -> [GitTag] {
+        do {
+            let output = try await run("for-each-ref refs/tags --format='%(refname:short) %(objectname) %(taggername) <%(taggeremail)> %(taggerdate)'")
+            var gitTags: [GitTag] = []
+            let lines = output.components(separatedBy: "\n")
+            for line in lines where !line.isEmpty {
+                // First, separate the line by the known structure "<email> <date>",
+                // which allows us to isolate the taggerName even if it contains spaces.
+                if let emailStartIndex = line.firstIndex(of: "<"),
+                   let emailEndIndex = line.firstIndex(of: ">") {
+                    let beforeEmail = line[..<emailStartIndex]
+                    let email = line[emailStartIndex...emailEndIndex]
+                    let afterEmail = line[line.index(after: emailEndIndex)...]
+                    let beforeEmailComponents = beforeEmail.split(separator: " ", maxSplits: 2, omittingEmptySubsequences: true).map(String.init)
+                    if beforeEmailComponents.count >= 2 {
+                        let name = beforeEmailComponents[0]
+                        let hash = beforeEmailComponents[1]
+                        let taggerName = beforeEmailComponents.dropFirst(2).joined(separator: " ")
+                        let tag = GitTag(name: name,
+                                         hash: hash,
+                                         taggerName: taggerName,
+                                         taggerEmail: String(email).trimmingCharacters(in: CharacterSet(charactersIn: "<>")),
+                                         dateCreated: String(afterEmail.trimmingCharacters(in: .whitespaces).dropFirst(2)))
+                        gitTags.append(tag)
+                    }
+                }
+            }
+            print(gitTags)
+            return gitTags
+        } catch {
+            print("Failed to execute git command: \(error)")
+            return []
+        }
+    }
+}
diff --git a/CodeEdit/Features/Git/Client/Models/GitTag.swift b/CodeEdit/Features/Git/Client/Models/GitTag.swift
new file mode 100644
index 000000000..3a6f62518
--- /dev/null
+++ b/CodeEdit/Features/Git/Client/Models/GitTag.swift
@@ -0,0 +1,16 @@
+//
+//  GitTag.swift
+//  CodeEdit
+//
+//  Created by Johnathan Baird on 2/6/24.
+//
+
+import Foundation
+
+struct GitTag {
+    let name: String
+    let hash: String
+    let taggerName: String
+    let taggerEmail: String
+    let dateCreated: String
+}
diff --git a/CodeEdit/Features/Git/SourceControlManager.swift b/CodeEdit/Features/Git/SourceControlManager.swift
index 56fff5e94..0a9623bbd 100644
--- a/CodeEdit/Features/Git/SourceControlManager.swift
+++ b/CodeEdit/Features/Git/SourceControlManager.swift
@@ -33,6 +33,9 @@ final class SourceControlManager: ObservableObject {
     /// All stash ebtrues
     @Published var stashEntries: [GitStashEntry] = []
 
+    /// All Tags
+    @Published var tags: [GitTag] = []
+
     /// Number of unsynced commits with remote in current branch
     @Published var numberOfUnsyncedCommits: (ahead: Int, behind: Int) = (ahead: 0, behind: 0)
 
@@ -332,6 +335,19 @@ final class SourceControlManager: ObservableObject {
         await showAlert(title: title, message: error.localizedDescription)
     }
 
+    /// Gets all tags
+    func refreshTags() async throws {
+        let tags = (try? await gitClient.getTags()) ?? []
+        await MainActor.run {
+            self.tags = tags
+        }
+    }
+
+    func newTag(tagName: String, commitHash: String, message: String?) async throws{
+        try await gitClient.createTag(tagName: tagName, commitHash: commitHash, message: message ?? "")
+        try await refreshTags()
+    }
+
     private func showAlert(title: String, message: String) async {
         await MainActor.run {
             let alert = NSAlert()
diff --git a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitListItemView.swift b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitListItemView.swift
index 8a19f67e2..9c5a80402 100644
--- a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitListItemView.swift
+++ b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/CommitListItemView.swift
@@ -8,11 +8,13 @@
 import SwiftUI
 
 struct CommitListItemView: View {
-
+    @EnvironmentObject var sourceControlManager: SourceControlManager
     var commit: GitCommit
 
     @Environment(\.openURL)
     private var openCommit
+    
+    @State var showNewTag: Bool = false
 
     init(commit: GitCommit) {
         self.commit = commit
@@ -30,21 +32,37 @@ struct CommitListItemView: View {
             }
             Spacer()
             VStack(alignment: .trailing, spacing: 5) {
-                Text(commit.hash)
-                    .font(.system(size: 10, design: .monospaced))
-                    .background(
-                        RoundedRectangle(cornerRadius: 3)
-                            .padding(.vertical, -1)
-                            .padding(.horizontal, -2.5)
-                            .foregroundColor(Color(nsColor: .quaternaryLabelColor))
-                    )
-                    .padding(.trailing, 2.5)
+                HStack {
+                    if let matchingTag = sourceControlManager.tags.first(where: { $0.hash == commit.commitHash }) {
+                        (Text(Image(systemName: "tag")) + Text(matchingTag.name))
+                            .font(.system(size: 10, design: .monospaced))
+                            .background(
+                                RoundedRectangle(cornerRadius: 3)
+                                    .padding(.vertical, -1)
+                                    .padding(.horizontal, -2.5)
+                                    .foregroundColor(Color(nsColor: .systemPurple))
+                            )
+                            .padding(.trailing, 2.5)
+                    }
+                    Text(commit.hash)
+                        .font(.system(size: 10, design: .monospaced))
+                        .background(
+                            RoundedRectangle(cornerRadius: 3)
+                                .padding(.vertical, -1)
+                                .padding(.horizontal, -2.5)
+                                .foregroundColor(Color(nsColor: .quaternaryLabelColor))
+                        )
+                        .padding(.trailing, 2.5)
+                }
                 Text(commit.date.relativeStringToNow())
                     .font(.system(size: 11))
                     .foregroundColor(.secondary)
             }
             .padding(.top, 1)
         }
+        .sheet(isPresented: $showNewTag) {
+            SourceControlNavigatorNewTagView(commitHash: commit.hash)
+        }
         .padding(.vertical, 1)
         .contentShape(Rectangle())
         .contextMenu {
@@ -67,8 +85,10 @@ struct CommitListItemView: View {
                 Divider()
             }
             Group {
-                Button("Tag \(commit.hash)...") {}
-                    .disabled(true) // TODO: Implementation Needed
+                Button("Tag \(commit.hash)...") {
+                    showNewTag = true
+                }
+                    // .disabled(true) // TODO: Implementation Needed
                 Button("New Branch from \(commit.hash)...") {}
                     .disabled(true) // TODO: Implementation Needed
                 Button("Cherry-Pick \(commit.hash)...") {}
diff --git a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/SourceControlNavigatorAddTagView.swift b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/SourceControlNavigatorAddTagView.swift
new file mode 100644
index 000000000..d858122fb
--- /dev/null
+++ b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/History/Views/SourceControlNavigatorAddTagView.swift
@@ -0,0 +1,65 @@
+//
+//  SourceControlNavigatorAddTagView.swift
+//  CodeEdit
+//
+//  Created by Johnathan Baird on 2/8/24.
+//
+import SwiftUI
+struct SourceControlNavigatorNewTagView: View {
+    @EnvironmentObject var sourceControlManager: SourceControlManager
+    @Environment(\.dismiss)
+    var dismiss
+
+    @State var name: String = ""
+    let commitHash: String
+    @State var message: String = ""
+
+    func submit() {
+        Task {
+            do {
+                try await sourceControlManager.newTag(tagName: name, commitHash: commitHash, message: message)
+                await MainActor.run {
+                    dismiss()
+                }
+            } catch {
+                await sourceControlManager.showAlertForError(
+                    title: "Failed to create tag",
+                    error: error
+                )
+            }
+        }
+    }
+
+    var body: some View {
+            VStack(spacing: 0) {
+                Form {
+                    Section {
+                        LabeledContent("Revision:", value: commitHash)
+                        TextField("Tag:", text: $name)
+                        TextField("Message:", text: $message)
+                    } header: {
+                        Text("Create a new tag from revision")
+                    }
+                }
+                //.formStyle(.grouped)
+                .scrollDisabled(true)
+                .scrollContentBackground(.hidden)
+                .onSubmit { submit() }
+                HStack {
+                    Spacer()
+                    Button("Cancel") {
+                        dismiss()
+                    }
+                    Button("Create") {
+                        submit()
+                    }
+                    .buttonStyle(.borderedProminent)
+                    .disabled(name.isEmpty)
+                }
+                .padding(.horizontal, 20)
+                .padding(.top, 12)
+                .padding(.bottom, 20)
+            }
+            .frame(maxWidth: 480)
+    }
+}
diff --git a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Repository/Views/SourceControlNavigatorRepositoryView+outlineGroupData.swift b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Repository/Views/SourceControlNavigatorRepositoryView+outlineGroupData.swift
index 7b698c315..cd18437f5 100644
--- a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Repository/Views/SourceControlNavigatorRepositoryView+outlineGroupData.swift
+++ b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Repository/Views/SourceControlNavigatorRepositoryView+outlineGroupData.swift
@@ -26,6 +26,21 @@ extension SourceControlNavigatorRepositoryView {
                         )
                 }
             ),
+            .init(
+                id: "TagsGroup",
+                label: "Tags",
+                systemImage: "tag.square.fill",
+                imageColor: Color(nsColor: .secondaryLabelColor),
+                children: sourceControlManager.tags.map { tag in
+                        .init(
+                            id: "Tag \(tag)",
+                            label: tag.name,
+                            description: tag.dateCreated,
+                            systemImage: "tag",
+                            imageColor: .purple
+                        )
+                }
+            ),
             .init(
                 id: "StashedChangesGroup",
                 label: "Stashed Changes",
diff --git a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Views/SourceControlNavigatorView.swift b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Views/SourceControlNavigatorView.swift
index 627194128..bfd4cc7eb 100644
--- a/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Views/SourceControlNavigatorView.swift
+++ b/CodeEdit/Features/NavigatorArea/SourceControlNavigator/Views/SourceControlNavigatorView.swift
@@ -52,6 +52,7 @@ struct SourcControlNavigatorTabs: View {
                 do {
                     try await sourceControlManager.refreshRemotes()
                     try await sourceControlManager.refreshStashEntries()
+                    try await sourceControlManager.refreshTags()
                 } catch {
                     await sourceControlManager.showAlertForError(title: "Error refreshing Git data", error: error)
                 }
