From 4f7f6a073ced1b31be6bbac119d1f0f4de229578 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Mon, 11 Mar 2019 13:04:08 +1100 Subject: allow user to discard old file changes for a given commit --- pkg/commands/errors.go | 14 +++++++++ pkg/commands/git.go | 77 ++++++++++++++++++++++++++++++++++++++++++++++---- pkg/commands/os.go | 14 ++++----- 3 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 pkg/commands/errors.go (limited to 'pkg/commands') diff --git a/pkg/commands/errors.go b/pkg/commands/errors.go new file mode 100644 index 000000000..4723eb95d --- /dev/null +++ b/pkg/commands/errors.go @@ -0,0 +1,14 @@ +package commands + +import "github.com/go-errors/errors" + +// WrapError wraps an error for the sake of showing a stack trace at the top level +// the go-errors package, for some reason, does not return nil when you try to wrap +// a non-error, so we're just doing it here +func WrapError(err error) error { + if err == nil { + return err + } + + return errors.Wrap(err, 0) +} diff --git a/pkg/commands/git.go b/pkg/commands/git.go index 96b25316c..c4de147cd 100644 --- a/pkg/commands/git.go +++ b/pkg/commands/git.go @@ -32,11 +32,11 @@ func navigateToRepoRootDirectory(stat func(string) (os.FileInfo, error), chdir f } if !os.IsNotExist(err) { - return errors.Wrap(err, 0) + return WrapError(err) } if err = chdir(".."); err != nil { - return errors.Wrap(err, 0) + return WrapError(err) } } } @@ -797,10 +797,25 @@ func (c *GitCommand) CherryPickCommits(commits []*Commit) error { return c.OSCommand.RunPreparedCommand(cmd) } -// CommitFiles get the specified commit files -func (c *GitCommand) CommitFiles(commitID string) (string, error) { +// GetCommitFiles get the specified commit files +func (c *GitCommand) GetCommitFiles(commitID string) ([]*CommitFile, error) { cmd := fmt.Sprintf("git show --pretty= --name-only %s", commitID) - return c.OSCommand.RunCommandWithOutput(cmd) + files, err := c.OSCommand.RunCommandWithOutput(cmd) + if err != nil { + return nil, err + } + + commitFiles := make([]*CommitFile, 0) + + for _, file := range strings.Split(strings.TrimRight(files, "\n"), "\n") { + commitFiles = append(commitFiles, &CommitFile{ + Sha: commitID, + Name: file, + DisplayString: file, + }) + } + + return commitFiles, nil } // ShowCommitFile get the diff of specified commit file @@ -814,3 +829,55 @@ func (c *GitCommand) CheckoutFile(commitSha, fileName string) error { cmd := fmt.Sprintf("git checkout %s %s", commitSha, fileName) return c.OSCommand.RunCommand(cmd) } + +// DiscardOldFileChanges discards changes to a file from an old commit +func (c *GitCommand) DiscardOldFileChanges(commits []*Commit, commitIndex int, fileName string) error { + // we can make this GPG thing possible it just means we need to do this in two parts: + // one where we handle the possibility of a credential request, and the other + // where we continue the rebase + if c.usingGpg() { + errors.New("feature not available for users using GPG") + } + + commitSha := commits[commitIndex].Sha + + todo, err := c.GenerateGenericRebaseTodo(commits, commitIndex, "edit") + if err != nil { + return err + } + + cmd, err := c.PrepareInteractiveRebaseCommand(commitSha+"^", todo, true) + if err != nil { + return err + } + + if err := c.OSCommand.RunPreparedCommand(cmd); err != nil { + return err + } + + // check if file exists in previous commit (this command returns an error if the file doesn't exist) + if err := c.OSCommand.RunCommand(fmt.Sprintf("git cat-file -e HEAD^:%s", fileName)); err != nil { + if err := c.OSCommand.RemoveFile(fileName); err != nil { + return err + } + if err := c.StageFile(fileName); err != nil { + return err + } + } else { + if err := c.CheckoutFile("HEAD^", fileName); err != nil { + return err + } + } + + // amend the commit + cmd, err = c.AmendHead() + if cmd != nil { + errors.New("received unexpected pointer to cmd") + } + if err != nil { + return err + } + + // continue + return c.GenericMerge("rebase", "continue") +} diff --git a/pkg/commands/os.go b/pkg/commands/os.go index a7a1fdaa4..9e0967cba 100644 --- a/pkg/commands/os.go +++ b/pkg/commands/os.go @@ -145,7 +145,7 @@ func sanitisedCommandOutput(output []byte, err error) (string, error) { // errors like 'exit status 1' are not very useful so we'll create an error // from the combined output if outputString == "" { - return "", errors.Wrap(err, 0) + return "", WrapError(err) } return outputString, errors.New(outputString) } @@ -224,13 +224,13 @@ func (c *OSCommand) Unquote(message string) string { func (c *OSCommand) AppendLineToFile(filename, line string) error { f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) if err != nil { - return errors.Wrap(err, 0) + return WrapError(err) } defer f.Close() _, err = f.WriteString("\n" + line) if err != nil { - return errors.Wrap(err, 0) + return WrapError(err) } return nil } @@ -240,16 +240,16 @@ func (c *OSCommand) CreateTempFile(filename, content string) (string, error) { tmpfile, err := ioutil.TempFile("", filename) if err != nil { c.Log.Error(err) - return "", errors.Wrap(err, 0) + return "", WrapError(err) } if _, err := tmpfile.WriteString(content); err != nil { c.Log.Error(err) - return "", errors.Wrap(err, 0) + return "", WrapError(err) } if err := tmpfile.Close(); err != nil { c.Log.Error(err) - return "", errors.Wrap(err, 0) + return "", WrapError(err) } return tmpfile.Name(), nil @@ -258,7 +258,7 @@ func (c *OSCommand) CreateTempFile(filename, content string) (string, error) { // RemoveFile removes a file at the specified path func (c *OSCommand) RemoveFile(filename string) error { err := os.Remove(filename) - return errors.Wrap(err, 0) + return WrapError(err) } // FileExists checks whether a file exists at the specified path -- cgit v1.2.3