Skip to content

Commit

Permalink
more restore
Browse files Browse the repository at this point in the history
  • Loading branch information
richardjennings committed Oct 28, 2024
1 parent d0ae09d commit 27cf96c
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 62 deletions.
2 changes: 1 addition & 1 deletion cmd/gitg/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ var restoreCmd = &cobra.Command{
}

func init() {
restoreCmd.Flags().BoolVar(&restoreStaged, "staged", true, "--staged")
restoreCmd.Flags().BoolVar(&restoreStaged, "staged", false, "--staged")
rootCmd.AddCommand(restoreCmd)
}
16 changes: 10 additions & 6 deletions index.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os"
"path/filepath"
"runtime"
"sort"
"syscall"
)

Expand Down Expand Up @@ -170,13 +171,9 @@ func (idx *Index) addFromWorkTree(f *FileStatus) error {
}

func (idx *Index) upsertItem(item *indexItem) error {
for k, v := range idx.items {
if bytes.Equal(v.Name, item.Name) {
idx.items[k] = item
return nil
}
if err := idx.updateItem(item); err != nil {
idx.addItem(item)
}
idx.items = append(idx.items, item)
return nil
}

Expand Down Expand Up @@ -207,9 +204,16 @@ func (idx *Index) Add(f *FileStatus) error {

// Write writes an Index struct to the Git Index
func (idx *Index) Write() error {

if idx.header.NumEntries != uint32(len(idx.items)) {
return errors.New("index numEntries and length of items inconsistent")
}

// and sort @todo more efficient
sort.Slice(idx.items, func(i, j int) bool {
return string(idx.items[i].Name) < string(idx.items[j].Name)
})

path := IndexFilePath()
f, err := os.OpenFile(path, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0644)
if err != nil {
Expand Down
9 changes: 7 additions & 2 deletions integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ func Test_Integration(t *testing.T) {
assertStatus(t, map[string]IndexStatus{"c": AddedInIndex}, map[string]WDStatus{"c": IndexAndWorkingTreeMatch})
}

// restore a file
// restore --staged a file that is commited, has been modified, added to
// index
// restore a working tree file that is in a previous commit
{
// commit change to 'c'
_ = assertCreateCommit(t, &Commit{
Expand All @@ -168,7 +170,10 @@ func Test_Integration(t *testing.T) {
assertRestore(t, "c", true)
// check status
assertStatus(t, map[string]IndexStatus{"c": NotUpdated}, map[string]WDStatus{"c": WorktreeChangedSinceIndex})

// git restore c
assertRestore(t, "c", false)
// check status
assertStatus(t, map[string]IndexStatus{"c": NotUpdated}, map[string]WDStatus{"c": IndexAndWorkingTreeMatch})
}

}
Expand Down
78 changes: 25 additions & 53 deletions restore.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package g

import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
)

// RestoreStaged removes a staged change from the index.
// If the file is in the previous commit, removing it from the index means
// updating the index to specify the commit sha. The timestamp for which would
// be the same as when the files were originally switched to.
//
// If the file is not in a previous commit, removing it from the index means
// simply removing it from the index.
func RestoreStaged(path string) error {
// update the index with the sha from the last commit
idx, err := ReadIndex()
if err != nil {
return err
}
status, err := CurrentStatus()
if err != nil {
return err
Expand All @@ -22,6 +22,10 @@ func RestoreStaged(path string) error {
if !ok {
return fmt.Errorf("file %s not found in index", path)
}
idx, err := ReadIndex()
if err != nil {
return err
}
if f.commit == nil {
// if the file is not commited at all, the correct behaviour of staged
// is to simply remove the file form the index such that it is no longer
Expand All @@ -31,18 +35,14 @@ func RestoreStaged(path string) error {
}
return idx.Write()
}

item, err := newItem(f.wd.Finfo, f.commit.Sha, f.path)
if err != nil {
return err
}
// Work Tree files do not have a hash associated until they are added to the
// index. Therefore, we use filesystem modification time to compare work tree
// files against the index. We do not know the previous actual modification
// time from the commit - as git does not track timestamps like that outside
// of the local index. So - just subtract 1 from the working directory
// timestamp, so that the index is detected as different form the working
// tree.
item.MTimeN--
// @todo how does git handle this specfically ?
item.MTimeS = item.MTimeS - 100
item.CTimeS = item.CTimeS - 100
if err := idx.upsertItem(item); err != nil {
return err
}
Expand All @@ -53,58 +53,30 @@ func Restore(path string, staged bool) error {
if staged {
return RestoreStaged(path)
}
idx, err := ReadIndex()
if err != nil {
return err
}
currentCommit, err := CurrentCommit()
if err != nil {
return err
}
currentStatus, err := Status(idx, currentCommit)

currentStatus, err := CurrentStatus()
if err != nil {
return err
}

fileStatus, ok := currentStatus.Contains(path)

// if the path not found or is untracked working directory fileStatus then error
if !ok || fileStatus.WorkingDirectoryStatus() == Untracked {
return fmt.Errorf("error: pathspec '%s' did not match any fileStatus(s) known to git", path)
}

// if in index but not committed
if fileStatus.IndexStatus() == AddedInIndex && fileStatus.WorkingDirectoryStatus() != WorktreeChangedSinceIndex {
// there is nothing to do, right ? ...
// there is nothing to do
return nil
}

// update working directory fileStatus with object referenced by index
file := idx.File(path)
if file == nil {
// this should not happen
return errors.New("index did not return file for some reason")
}
obj, err := ReadObject(file.index.Sha)
if err != nil {
return err
}
fh, err := os.OpenFile(filepath.Join(Path(), path), os.O_TRUNC|os.O_RDWR, 0600)
if err != nil {
return err
}
defer func() { _ = fh.Close() }()
reader, err := obj.ReadCloser()
if err != nil {
// write the file
if err := writeObjectToWorkingTree(fileStatus.index.Sha, fileStatus.Path()); err != nil {
return err
}
if err := ReadHeadBytes(reader, obj); err != nil {
return err
}
_, err = io.Copy(fh, reader)
if err != nil {
return err
}
if err := fh.Close(); err != nil {
return err
}
return os.Chtimes(filepath.Join(Path(), path), file.index.Finfo.ModTime(), file.index.Finfo.ModTime())

// update modification time to match index
return os.Chtimes(filepath.Join(Path(), path), fileStatus.index.Finfo.ModTime(), fileStatus.index.Finfo.ModTime())
}

0 comments on commit 27cf96c

Please sign in to comment.