code.gitea.io/gitea@v1.22.3/modules/git/repo_index.go (about)

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package git
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  
    13  	"code.gitea.io/gitea/modules/log"
    14  	"code.gitea.io/gitea/modules/util"
    15  )
    16  
    17  // ReadTreeToIndex reads a treeish to the index
    18  func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) error {
    19  	objectFormat, err := repo.GetObjectFormat()
    20  	if err != nil {
    21  		return err
    22  	}
    23  
    24  	if len(treeish) != objectFormat.FullLength() {
    25  		res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify").AddDynamicArguments(treeish).RunStdString(&RunOpts{Dir: repo.Path})
    26  		if err != nil {
    27  			return err
    28  		}
    29  		if len(res) > 0 {
    30  			treeish = res[:len(res)-1]
    31  		}
    32  	}
    33  	id, err := NewIDFromString(treeish)
    34  	if err != nil {
    35  		return err
    36  	}
    37  	return repo.readTreeToIndex(id, indexFilename...)
    38  }
    39  
    40  func (repo *Repository) readTreeToIndex(id ObjectID, indexFilename ...string) error {
    41  	var env []string
    42  	if len(indexFilename) > 0 {
    43  		env = append(os.Environ(), "GIT_INDEX_FILE="+indexFilename[0])
    44  	}
    45  	_, _, err := NewCommand(repo.Ctx, "read-tree").AddDynamicArguments(id.String()).RunStdString(&RunOpts{Dir: repo.Path, Env: env})
    46  	if err != nil {
    47  		return err
    48  	}
    49  	return nil
    50  }
    51  
    52  // ReadTreeToTemporaryIndex reads a treeish to a temporary index file
    53  func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (filename, tmpDir string, cancel context.CancelFunc, err error) {
    54  	tmpDir, err = os.MkdirTemp("", "index")
    55  	if err != nil {
    56  		return filename, tmpDir, cancel, err
    57  	}
    58  
    59  	filename = filepath.Join(tmpDir, ".tmp-index")
    60  	cancel = func() {
    61  		err := util.RemoveAll(tmpDir)
    62  		if err != nil {
    63  			log.Error("failed to remove tmp index file: %v", err)
    64  		}
    65  	}
    66  	err = repo.ReadTreeToIndex(treeish, filename)
    67  	if err != nil {
    68  		defer cancel()
    69  		return "", "", func() {}, err
    70  	}
    71  	return filename, tmpDir, cancel, err
    72  }
    73  
    74  // EmptyIndex empties the index
    75  func (repo *Repository) EmptyIndex() error {
    76  	_, _, err := NewCommand(repo.Ctx, "read-tree", "--empty").RunStdString(&RunOpts{Dir: repo.Path})
    77  	return err
    78  }
    79  
    80  // LsFiles checks if the given filenames are in the index
    81  func (repo *Repository) LsFiles(filenames ...string) ([]string, error) {
    82  	cmd := NewCommand(repo.Ctx, "ls-files", "-z").AddDashesAndList(filenames...)
    83  	res, _, err := cmd.RunStdBytes(&RunOpts{Dir: repo.Path})
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  	filelist := make([]string, 0, len(filenames))
    88  	for _, line := range bytes.Split(res, []byte{'\000'}) {
    89  		filelist = append(filelist, string(line))
    90  	}
    91  
    92  	return filelist, err
    93  }
    94  
    95  // RemoveFilesFromIndex removes given filenames from the index - it does not check whether they are present.
    96  func (repo *Repository) RemoveFilesFromIndex(filenames ...string) error {
    97  	objectFormat, err := repo.GetObjectFormat()
    98  	if err != nil {
    99  		return err
   100  	}
   101  	cmd := NewCommand(repo.Ctx, "update-index", "--remove", "-z", "--index-info")
   102  	stdout := new(bytes.Buffer)
   103  	stderr := new(bytes.Buffer)
   104  	buffer := new(bytes.Buffer)
   105  	for _, file := range filenames {
   106  		if file != "" {
   107  			// using format: mode SP type SP sha1 TAB path
   108  			buffer.WriteString("0 blob " + objectFormat.EmptyObjectID().String() + "\t" + file + "\000")
   109  		}
   110  	}
   111  	return cmd.Run(&RunOpts{
   112  		Dir:    repo.Path,
   113  		Stdin:  bytes.NewReader(buffer.Bytes()),
   114  		Stdout: stdout,
   115  		Stderr: stderr,
   116  	})
   117  }
   118  
   119  type IndexObjectInfo struct {
   120  	Mode     string
   121  	Object   ObjectID
   122  	Filename string
   123  }
   124  
   125  // AddObjectsToIndex adds the provided object hashes to the index at the provided filenames
   126  func (repo *Repository) AddObjectsToIndex(objects ...IndexObjectInfo) error {
   127  	cmd := NewCommand(repo.Ctx, "update-index", "--add", "--replace", "-z", "--index-info")
   128  	stdout := new(bytes.Buffer)
   129  	stderr := new(bytes.Buffer)
   130  	buffer := new(bytes.Buffer)
   131  	for _, object := range objects {
   132  		// using format: mode SP type SP sha1 TAB path
   133  		buffer.WriteString(object.Mode + " blob " + object.Object.String() + "\t" + object.Filename + "\000")
   134  	}
   135  	return cmd.Run(&RunOpts{
   136  		Dir:    repo.Path,
   137  		Stdin:  bytes.NewReader(buffer.Bytes()),
   138  		Stdout: stdout,
   139  		Stderr: stderr,
   140  	})
   141  }
   142  
   143  // AddObjectToIndex adds the provided object hash to the index at the provided filename
   144  func (repo *Repository) AddObjectToIndex(mode string, object ObjectID, filename string) error {
   145  	return repo.AddObjectsToIndex(IndexObjectInfo{Mode: mode, Object: object, Filename: filename})
   146  }
   147  
   148  // WriteTree writes the current index as a tree to the object db and returns its hash
   149  func (repo *Repository) WriteTree() (*Tree, error) {
   150  	stdout, _, runErr := NewCommand(repo.Ctx, "write-tree").RunStdString(&RunOpts{Dir: repo.Path})
   151  	if runErr != nil {
   152  		return nil, runErr
   153  	}
   154  	id, err := NewIDFromString(strings.TrimSpace(stdout))
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	return NewTree(repo, id), nil
   159  }