Giter Club home page Giter Club logo

Comments (2)

maskimko avatar maskimko commented on May 17, 2024

Let me post my workaround of this bug in the client code

//go-git library has a bug in pull operation. When the directory contains unstaged files it stops the reset --merge operation on the half way, 
//which caused inconsistency in the working directory of git. To be precise all changes which come from new commits from remote repo, will be staged in reverted state
func pullRecover(gitRepo *git.Repository, pre, post *plumbing.Reference, preStatus *git.Status) *misc.RuntimeError {

	gwt, err := gitRepo.Worktree()
	gitPath := gwt.Filesystem.Root()
	if err != nil {
		return misc.NewRuntimeError("cannot get git worktree", 190, err)
	}
	postStatus, err := gwt.Status()
	if err != nil {
		helpers.Debugf("cannot get post git status. Error: %s", err)
		return misc.NewRuntimeError("cannot get post git status", 193, err)
	} else {
		helpers.Debugf("Git %s status after pull \n%s", gitPath, postStatus.String())
	}
	postCommitObject, err := gitRepo.CommitObject(post.Hash())
	if err != nil {
		helpers.Debugf("cannot get post commit object. Error: %s", err)
		return misc.NewRuntimeError("cannot get post commit object", 194, err)
	}
	postComTree, err := postCommitObject.Tree()
	if err != nil {
		helpers.Debugf("cannot get post commit tree. Error: %s", err)
		return misc.NewRuntimeError("cannot get post commit tree", 195, err)
	}
	preCommitObject, err := gitRepo.CommitObject(pre.Hash())
	if err != nil {
		helpers.Debugf("cannot get pre commit object. Error: %s", err)
		return misc.NewRuntimeError("cannot get pre commit object", 196, err)
	}
	preComTree, err := preCommitObject.Tree()
	if err != nil {
		helpers.Debugf("cannot get pre commit tree. Error: %s", err)
		return misc.NewRuntimeError("cannot get pre commit tree", 197, err)
	}
	changes, err := preComTree.Diff(postComTree)
	if err != nil {
		helpers.Debugf("failed to compute git diff. Error %s", err)
		return misc.NewRuntimeError("failed to compute git diff", 198, err)
	}

	successful := true
	//Check statuses

	for f, fs := range *preStatus {
		if fs.Staging == git.Unmodified && fs.Worktree == git.Modified {
			pfs := postStatus.File(f)
			if pfs.Worktree != fs.Worktree {
				helpers.Debugf("worktree status of the file %s has been changed after pull. Please merge manually", f)
				return misc.NewRuntimeError(fmt.Sprintf("worktree status of the file %s has been changed after pull", f), 192, nil)
			}
			if pfs.Staging != fs.Staging {
				helpers.Debugf("staging status of the file %s has been changed after pull. Please merge manually", f)
				return misc.NewRuntimeError(fmt.Sprintf("staging status of the file %s has been changed after pull", f), 192, nil)
			}
		}
	}
	for _, c := range changes {
		err = resetFile(gitRepo, c, post, false) //err = gwt.Checkout(&git.CheckoutOptions{Force: true, Hash: postHead.Hash()})
		if err != nil {
			helpers.Debugf("failed to reset file %s. Error %s", c.To.Name, err)
			successful = false
		}
	}
	if successful {
		return nil
	}
	return misc.NewRuntimeError("failed to recover the pull. Please fix git %s repo manually", 199, nil)
}

//resets a particular file to the correct version
func resetFile(repo *git.Repository, c *object.Change, ref *plumbing.Reference, reverse bool) error {
	var obj object.ChangeEntry
	if reverse {
		obj = c.From
	} else {
		obj = c.To
	}

	gwt, err := repo.Worktree()
	if err != nil {
		helpers.Debugf("cannot get git worktree. Error %s", err)
		return err
	}
	source, err := obj.Tree.TreeEntryFile(&obj.TreeEntry)
	if err != nil {
		helpers.Debugf("cannot get modified file from tree. Error %s", err)
		return err
	}
	//
	fMode, err := obj.TreeEntry.Mode.ToOSFileMode()
	if err != nil {
		helpers.Debugf("cannot get a file mode. Error: %s", err)
		return err
	}
	gitPath := gwt.Filesystem.Root()
	absPath := path.Join(gitPath, obj.Name)
	dest, err := gwt.Filesystem.OpenFile(obj.Name, os.O_CREATE|os.O_WRONLY, fMode)
	//dest, err := os.OpenFile(destPath, os.O_CREATE|os.O_WRONLY, fMode)
	if err != nil {
		helpers.Debugf("canot open destination file")
		return err
	}
	defer dest.Close()
	sourceReader, err := source.Reader()
	if err != nil {
		helpers.Debugf("failed to get reader from source file")
		return err
	}
	defer sourceReader.Close()
	//buf := make([]bwrittenyte, 2048)
	//_, err = io.CopyBuffer(dest, sourceReader, buf)
	err = dest.Truncate(0)
	if err != nil {
		helpers.Debugf("failed to truncate file %s Error: %s", obj.Name, err)
		return err
	}
	_, err = dest.Seek(0, 0)
	if err != nil {
		helpers.Debugf("failed to jump on the beginning of file %s Error: %s", obj.Name, err)
		return err
	}
	written, err := io.Copy(dest, sourceReader)
	if err != nil {
		helpers.Debugf("failed to copy file. Written: %d Error: %s", written, err)
		return err
	} else {
		helpers.Debugf("file %s has been restored to the %s state", obj.Name, ref.Name())
	}
	i, err := repo.Storer.Index()
	if err != nil {
		helpers.Debugf("cannot get i of repository. Error %s", err)
		return err
	}
	entry, err := i.Entry(obj.Name)
	if err != nil {
		helpers.Debugf("cannot get entry %s Error %s", obj.Name, err)
		return err
	}
	fi, err := os.Stat(absPath)
	if err != nil {
		helpers.Debugf("cannot stat %s file info. Error: %s", obj.Name, err)
		return err
	}
	_, err = i.Remove(obj.Name)
	if err != nil {
		helpers.Debugf("cannot remove index %s Error: %s", obj.Name, err)
	}
	helpers.Debug("Entry " + entry.Name)
	mode, err := filemode.NewFromOSFileMode(fMode)
	if err != nil {
		helpers.Debugf("cannot get file %s mode. Error %s", obj.Name, err)
	}
	e := index.Entry{
		Hash:       obj.TreeEntry.Hash,
		Name:       obj.Name,
		Mode:       mode,
		ModifiedAt: fi.ModTime(),
		Size:       uint32(fi.Size()),
	}
	i.Entries = append(i.Entries, &e)
	err = repo.Storer.SetIndex(i)
	if err != nil {
		helpers.Debugf("failed to set index. Error: %s", err)
	}
	return nil
}

from go-git.

github-actions avatar github-actions commented on May 17, 2024

To help us keep things tidy and focus on the active tasks, we've introduced a stale bot to spot issues/PRs that haven't had any activity in a while.

This particular issue hasn't had any updates or activity in the past 90 days, so it's been labeled as 'stale'. If it remains inactive for the next 30 days, it'll be automatically closed.

We understand everyone's busy, but if this issue is still important to you, please feel free to add a comment or make an update to keep it active.

Thanks for your understanding and cooperation!

from go-git.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.