Skip to content

Commit

Permalink
git restore --staged
Browse files Browse the repository at this point in the history
  • Loading branch information
richardjennings committed Oct 28, 2024
1 parent 6f7f7aa commit d5fe56d
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 10 deletions.
11 changes: 11 additions & 0 deletions index.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,17 @@ func (idx *Index) addFromWorkTree(f *FileStatus) error {
return nil
}

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
}
}
idx.items = append(idx.items, item)
return nil
}

func (idx *Index) updateItem(i *indexItem) error {
found := false
for k, v := range idx.items {
Expand Down
34 changes: 31 additions & 3 deletions integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,30 @@ func Test_Integration(t *testing.T) {
assertStatus(t, map[string]IndexStatus{"c": AddedInIndex}, map[string]WDStatus{"c": IndexAndWorkingTreeMatch})
}

// check for semantics when switching branch
//
// restore a file
{
// commit change to 'c'
_ = assertCreateCommit(t, &Commit{
Author: fmt.Sprintf("%s <%s>", "tester", "[email protected]"),
AuthoredTime: time.Now(),
Committer: fmt.Sprintf("%s <%s>", "tester", "[email protected]"),
CommittedTime: time.Now(),
Message: []byte("this is yet another commit message"),
})
// change 'c'
e(os.WriteFile(filepath.Join(dir, "c"), []byte("cc"), 0644), t)
// check status is correct
assertStatus(t, map[string]IndexStatus{"c": NotUpdated}, map[string]WDStatus{"c": WorktreeChangedSinceIndex})
// add to index
assertAddFiles(t, []string{"c"})
// check status
assertStatus(t, map[string]IndexStatus{"c": UpdatedInIndex}, map[string]WDStatus{"c": IndexAndWorkingTreeMatch})
// restore --staged c
assertRestore(t, "c", true)
// check status
assertStatus(t, map[string]IndexStatus{"c": NotUpdated}, map[string]WDStatus{"c": WorktreeChangedSinceIndex})

}

}

Expand All @@ -158,6 +180,12 @@ func e(err error, t *testing.T) {
}
}

func assertRestore(t *testing.T, path string, staged bool) {
if err := Restore(path, staged); err != nil {
t.Fatal(err)
}
}

func assertLookupCommit(t *testing.T, sha Sha, f func(*testing.T, *Commit)) {
t.Helper()
commit, err := ReadCommit(sha)
Expand Down Expand Up @@ -256,7 +284,7 @@ func assertStatus(t *testing.T, i map[string]IndexStatus, w map[string]WDStatus)
t.Fatalf("expected file '%s'", k)
}
if f.wdStatus != v {
t.Errorf("expected file '%s' to have working directory status '%d' got '%d'", k, v, f.wdStatus)
t.Errorf("expected file '%s' to have working directory status '%s' got '%s'", k, v, f.wdStatus)
}
}
}
42 changes: 35 additions & 7 deletions restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,42 @@ import (
"path/filepath"
)

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
}
f, ok := status.idx[path]
if !ok {
return fmt.Errorf("file %s not found in index", path)
}
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--
if err := idx.upsertItem(item); err != nil {
return err
}
return idx.Write()
}

func Restore(path string, staged bool) error {
if staged {
return RestoreStaged(path)
}
idx, err := ReadIndex()
if err != nil {
return err
Expand All @@ -21,13 +56,6 @@ func Restore(path string, staged bool) error {
if err != nil {
return err
}
if staged {
// remove file from index
if err := idx.Rm(path); err != nil {
return err
}
return idx.Write()
}

fileStatus, ok := currentStatus.Contains(path)
// if the path not found or is untracked working directory fileStatus then error
Expand Down

0 comments on commit d5fe56d

Please sign in to comment.