Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 52 additions & 21 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func NewAppGiBack(version string, verbose, dryRun bool) *App {
// ANSI escape code for gray color.
const (
yellowColor = "\033[33m"
orangeColor = "\033[38;5;208m"
grayColor = "\033[90m"
redColor = "\033[31m"
resetColor = "\033[0m"
Expand Down Expand Up @@ -108,9 +109,14 @@ func (a *App) logDebugf(format string, args ...any) {
_, _ = fmt.Fprintf(os.Stderr, yellowColor+a.getAppName()+" ⚙️: "+grayColor+format+resetColor+"\n", args...)
}

// logWarnf writes error messages to stderr.
// logErrorf writes error messages to stderr.
func (a *App) logErrorf(format string, args ...any) {
_, _ = fmt.Fprintf(os.Stderr, redColor+a.getAppName()+" ❌️: "+grayColor+format+resetColor+"\n", args...)
}

// logWarnf writes warning (soft error) messages to stderr.
func (a *App) logWarnf(format string, args ...any) {
_, _ = fmt.Fprintf(os.Stderr, redColor+a.getAppName()+" : "+grayColor+format+resetColor+"\n", args...)
_, _ = fmt.Fprintf(os.Stderr, orangeColor+a.getAppName()+" ⚠️: "+grayColor+format+resetColor+"\n", args...)
}

// logInfof writes info messages to stderr.
Expand Down Expand Up @@ -168,7 +174,7 @@ func (a *App) Run(args []string) (err error) {
// Get the last undoed entry (from current reference)
lastEntry, err := lgr.GetLastEntry()
if err != nil {
a.logWarnf("something wrong with the log: %v", err)
a.logErrorf("something wrong with the log: %v", err)
return nil
}
if lastEntry == nil || !lastEntry.Undoed {
Expand Down Expand Up @@ -201,15 +207,28 @@ func (a *App) Run(args []string) (err error) {
// Get the last git command
var lastEntry *logging.Entry
if a.isBackMode {
// For git-back, only look for checkout/switch commands
lastEntry, err = lgr.GetLastCheckoutSwitchEntry()
// For git-back, look for the last checkout/switch command (including undoed ones for toggle behavior)
// We pass "any" to look across all refs, not just the current one
lastEntry, err = lgr.GetLastEntry(logging.RefAny)
if err != nil {
return fmt.Errorf("failed to get last checkout/switch command: %w", err)
return fmt.Errorf("failed to get last command: %w", err)
}
if lastEntry == nil {
a.logDebugf("no checkout/switch commands to undo")
a.logDebugf("no commands found")
return nil
}
// Check if the last command was a checkout or switch command
if !a.isCheckoutOrSwitchCommand(lastEntry.Command) {
// If not, try to find the last checkout/switch command (including undoed ones for toggle behavior)
lastEntry, err = lgr.GetLastCheckoutSwitchEntryForToggle(logging.RefAny)
if err != nil {
return fmt.Errorf("failed to get last checkout/switch command: %w", err)
}
if lastEntry == nil {
a.logDebugf("no checkout/switch commands to undo")
return nil
}
}
} else {
// For git-undo, get any regular entry
lastEntry, err = lgr.GetLastRegularEntry()
Expand Down Expand Up @@ -238,37 +257,49 @@ func (a *App) Run(args []string) (err error) {
u = undoer.New(lastEntry.Command, g)
}

// Get the undo command
undoCmd, err := u.GetUndoCommand()
// Get the undo commands
undoCmds, err := u.GetUndoCommands()
if err != nil {
return err
}

if a.dryRun {
a.logDebugf("Would run: %s\n", undoCmd.Command)
if len(undoCmd.Warnings) > 0 {
for _, warning := range undoCmd.Warnings {
a.logWarnf("%s", warning)
for _, undoCmd := range undoCmds {
a.logDebugf("Would run: %s\n", undoCmd.Command)
if len(undoCmd.Warnings) > 0 {
for _, warning := range undoCmd.Warnings {
a.logWarnf("%s", warning)
}
}
}
return nil
}

// Execute the undo command
if err := undoCmd.Exec(); err != nil {
return fmt.Errorf("failed to execute undo command %s via %s: %w", lastEntry.Command, undoCmd.Command, err)
// Execute the undo commands
for i, undoCmd := range undoCmds {
if err := undoCmd.Exec(); err != nil {
return fmt.Errorf("failed to execute undo command %d/%d %s via %s: %w",
i+1, len(undoCmds), lastEntry.Command, undoCmd.Command, err)
}
a.logDebugf("Successfully executed undo command %d/%d: %s via %s",
i+1, len(undoCmds), lastEntry.Command, undoCmd.Command)
if len(undoCmd.Warnings) > 0 {
for _, warning := range undoCmd.Warnings {
a.logWarnf("%s", warning)
}
}
}

// Mark the entry as undoed in the log
if err := lgr.ToggleEntry(lastEntry.GetIdentifier()); err != nil {
a.logWarnf("Failed to mark command as undoed: %v", err)
}

a.logDebugf("Successfully undid: %s via %s", lastEntry.Command, undoCmd.Command)
if len(undoCmd.Warnings) > 0 {
for _, warning := range undoCmd.Warnings {
a.logWarnf("%s", warning)
}
// Summary message for all commands
if len(undoCmds) == 1 {
a.logDebugf("Successfully undid: %s via %s", lastEntry.Command, undoCmds[0].Command)
} else {
a.logDebugf("Successfully undid: %s via %d commands", lastEntry.Command, len(undoCmds))
}
return nil
}
Expand Down
14 changes: 10 additions & 4 deletions internal/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,17 @@ func (s *GitTestSuite) TestUndoAdd() {

// TestSequentialUndo tests multiple undo operations in sequence.
func (s *GitTestSuite) TestSequentialUndo() {
s.T().Skip("TODO FIX ME ")
// Setup: Create an initial base commit so we're not working from the root commit
initialFile := filepath.Join(s.GetRepoDir(), "initial.txt")
err := os.WriteFile(initialFile, []byte("initial content"), 0644)
s.Require().NoError(err)
s.Git("add", filepath.Base(initialFile))
s.Git("commit", "-m", "Initial base commit")

// Create test files
file1 := filepath.Join(s.GetRepoDir(), "file1.txt")
file2 := filepath.Join(s.GetRepoDir(), "file2.txt")
err := os.WriteFile(file1, []byte("content1"), 0644)
err = os.WriteFile(file1, []byte("content1"), 0644)
s.Require().NoError(err)
err = os.WriteFile(file2, []byte("content2"), 0644)
s.Require().NoError(err)
Expand All @@ -145,6 +150,7 @@ func (s *GitTestSuite) TestSequentialUndo() {
s.gitUndo()
status = s.RunCmd("git", "status", "--porcelain")
s.Contains(status, "A file2.txt", "file2.txt should be staged after undoing second commit")
s.NotContains(status, "A file1.txt", "file1.txt should not be staged after undoing second commit")

// Second undo: should unstage file2.txt
s.gitUndo()
Expand Down Expand Up @@ -174,12 +180,12 @@ func (s *GitTestSuite) TestUndoLog() {

// Perform some git operations to generate log entries
s.Git("add", filepath.Base(testFile)) // Use relative path for git commands
s.Git("commit", "-m", "'First commit'")
s.Git("commit", "-m", "First commit")

// Check that log command works and shows output
log := s.gitUndoLog()
s.NotEmpty(log, "Log should not be empty")
s.Contains(log, "git commit -m 'First commit'", "Log should contain commit command")
s.Contains(log, "git commit -m First commit", "Log should contain commit command")
s.Contains(log, "git add test.txt", "Log should contain add command")
s.Contains(log, "|feature-branch|", "Log should contain branch name")

Expand Down
Loading
Loading