func writeIndexEntries(repo *libgit.Repository, prefix string, entries []*GitIndexEntry) ([20]byte, error) { content := bytes.NewBuffer(nil) // [mode] [file/folder name]\0[SHA-1 of referencing blob or tree as [20]byte] lastname := "" firstIdxForTree := -1 for idx, obj := range entries { nameBits := strings.Split(obj.PathName, "/") // Either it's the last entry and we haven't written a tree yet, or it's not the last // entry but the directory changed if (nameBits[0] != lastname || idx == len(entries)-1) && lastname != "" { var islice []*GitIndexEntry if idx == len(entries)-1 { islice = entries[firstIdxForTree:] } else { islice = entries[firstIdxForTree:idx] } subsha1, err := writeIndexSubtree(repo, lastname, islice) if err != nil { panic(err) } // Write the object fmt.Fprintf(content, "%o %s\x00", 0040000, lastname) content.Write(subsha1[:]) // Reset the data keeping track of what this tree is. lastname = "" firstIdxForTree = -1 } if len(nameBits) == 1 { //write the blob for the file portion fmt.Fprintf(content, "%o %s\x00", obj.Mode, obj.PathName) content.Write(obj.Sha1[:]) lastname = "" } else { lastname = nameBits[0] if firstIdxForTree == -1 { firstIdxForTree = idx } } } sha1, err := repo.StoreObjectLoose(libgit.ObjectTree, bytes.NewReader(content.Bytes())) return [20]byte(sha1), err }
func CommitTree(repo *libgit.Repository, args []string) string { content := bytes.NewBuffer(nil) var parents []string var messageString, messageFile string var skipNext bool var tree string for idx, val := range args { if idx == 0 && val[0] != '-' { tree = val continue } if skipNext == true { skipNext = false continue } switch val { case "-p": parents = append(parents, args[idx+1]) skipNext = true case "-m": messageString += "\n" + args[idx+1] + "\n" skipNext = true case "-F": messageFile = args[idx+1] skipNext = true } } if messageString == "" { if messageFile != "" { // TODO: READ commit message from messageFile here } else { // If neither messageString nor messageFile are set, read // from STDIN } panic("Must provide message with -m parameter to commit-tree") } if tree == "" { tree = args[len(args)-1] } // TODO: Validate tree id fmt.Fprintf(content, "tree %s\n", tree) for _, val := range parents { fmt.Fprintf(content, "parent %s\n", val) } author := getAuthor(repo) t := time.Now() _, tzoff := t.Zone() // for some reason t.Zone() returns the timezone offset in seconds // instead of hours, so convert it to an hour format string tzStr := fmt.Sprintf("%+03d00", tzoff/(60*60)) fmt.Fprintf(content, "author %s %d %s\n", author, t.Unix(), tzStr) fmt.Fprintf(content, "committer %s %d %s\n", author, t.Unix(), tzStr) fmt.Fprintf(content, "%s", messageString) fmt.Printf("%s", content.Bytes()) sha1, err := repo.StoreObjectLoose(libgit.ObjectCommit, bytes.NewReader(content.Bytes())) if err != nil { return "" } return fmt.Sprintf("%s", sha1) }
// Adds a file to the index, without writing it to disk. // To write it to disk after calling this, use GitIndex.WriteIndex // // This will do the following: // write git object blob of file contents to .git/objects // normalize os.File name to path relative to gitRoot // search GitIndex for normalized name // if GitIndexEntry found // update GitIndexEntry to point to the new object blob // else // add new GitIndexEntry if not found // func (g *GitIndex) AddFile(repo *libgit.Repository, file *os.File) { sha1, err := repo.StoreObjectLoose(libgit.ObjectBlob, file) if err != nil { fmt.Fprintf(os.Stderr, "Error storing object: %s", err) } fmt.Printf("Sha1: %s\n", sha1) fmt.Printf("Name is %s\n", file.Name()) name := getNormalizedName(file) for _, entry := range g.Objects { if entry.PathName == name { entry.Sha1 = sha1 fstat, err := file.Stat() if err != nil { panic(err) } modTime := fstat.ModTime() entry.Mtime = uint32(modTime.Unix()) entry.Mtimenano = uint32(modTime.Nanosecond()) entry.Fsize = uint32(fstat.Size()) return } } fstat, err := file.Stat() if err != nil { panic(err) } modTime := fstat.ModTime() stat := fstat.Sys().(*syscall.Stat_t) csec, cnano := stat.Ctim.Unix() var mode uint32 if fstat.IsDir() { // This should really recursively call add for each file in the directory. panic("Add can't handle directories. yet.") } else { // if it's a regular file, just assume it's 0644 for now mode = 0x81A4 } // mode is /* 32-bit mode, split into (high to low bits) 4-bit object type valid values in binary are 1000 (regular file), 1010 (symbolic link) and 1110 (gitlink) 3-bit unused 9-bit unix permission. Only 0755 and 0644 are valid for regular files. Symbolic links and gitlinks have value 0 in this field. Flags is A 16-bit 'flags' field split into (high to low bits) 1-bit assume-valid flag 1-bit extended flag (must be zero in version 2) 2-bit stage (during merge) 12-bit name length if the length is less than 0xFFF; otherwise 0xFFF is stored in this field. */ //var flags uint16 = 0x8000 // start with "assume-valid" flag var flags uint16 = 0x8000 // start with "assume-valid" flag if len(name) >= 0x0FFF { flags |= 0x0FFF } else { flags |= (uint16(len(name)) & 0x0FFF) } g.Objects = append(g.Objects, &GitIndexEntry{ fixedIndexEntry{ uint32(csec), // right? uint32(cnano), // right? uint32(modTime.Unix()), // right uint32(modTime.Nanosecond()), // right uint32(stat.Dev), // right uint32(stat.Ino), // right mode, stat.Uid, // right? stat.Gid, // right? uint32(fstat.Size()), // right sha1, // this is right flags, // right }, name, }) g.NumberIndexEntries += 1 sort.Sort(ByPath(g.Objects)) return }