1

I'm trying to create a git tree and check it out without using any high-level commands and commit objects. So I initialized git and create two blobs:

$ git init

$ echo ‘f1’ | git hash-object -w --stdin
8e1e71d5ce34c01b6fe83bc5051545f2918c8c2b

$ echo ‘f2’ | git hash-object -w --stdin
9de77c18733ab8009a956c25e28c85fe203a17d7

Now I'm creating a tree using mktree:

$ echo -e 
    "100644 blob 8e1e71d5ce34c01b6fe83bc5051545f2918c8c2b\tf1.txt" 
    "100644 blob 9de77c18733ab8009a956c25e28c85fe203a17d7\tf2.txt" 
    | git mktree
bf9571850c4570cd36ffa426343b81364a855911

Which correctly returns me the git tree hash. Now I want to check it out. According to this answer I can simply do it like this:

$ git checkout bf9571850c4570cd36ffa426343b81364a855911 .

But it produces the error:

error: unable to create file f1.txt 100644 blob 9de77c18733ab8009a956c25e28c85fe203a17d7        f2.txt (Invalid argument)

Can anyone help?

Max Koretskyi
  • 101,079
  • 60
  • 333
  • 488

1 Answers1

1

There's something slightly wonky in your instructions, because if I retype:

$ echo 'f1' | git hash-object -w --stdin

I get the same hash:

8e1e71d5ce34c01b6fe83bc5051545f2918c8c2b

but if I try to cut and paste them, I get something very different. This is because something (I'm not sure what) has damaged the single quotes: yours are actually Unicode U+2018 characters, or LEFT SINGLE QUOTATION MARK.

Anyway, as a result I was not quite sure what to do with your third echo command, but if I assume it's undamaged, it's actually sending:

100644 blob 8e1e71d5ce34c01b6fe83bc5051545f2918c8c2b\tf1.txt 100644 blob 9de77c18733ab8009a956c25e28c85fe203a17d7\tf2.txt

(where each \t represents a literal tab character) to git mktree, which will make a tree with one entry. Trying that gives me:

$ printf "100644 blob 8e1e71d5ce34c01b6fe83bc5051545f2918c8c2b\tf1.txt 100644 blob 9de77c18733ab8009a956c25e28c85fe203a17d7\tf2.txt\n" | git mktree
bf9571850c4570cd36ffa426343b81364a855911

which has the same tree-hash, and:

$ git cat-file -p bf9571850
100644 blob 8e1e71d5ce34c01b6fe83bc5051545f2918c8c2b    "f1.txt 100644 blob 9de77c18733ab8009a956c25e28c85fe203a17d7\tf2.txt"

(I've left a raw tab in this stackoverflow input, which is a little bit tricky but works with cut-and-paste on a Mac).

Checking out that tree into the current directory will attempt to create a file named f1.txt 100644 blob 9de77c18733ab8009a956c25e28c85fe203a17d7\tf2.txt (with embedded tab in the name) as one should expect. Presumably your host OS (Windows?) refuses such file names.

Presumably, what you wanted was to print two lines into git mktree, one each for the f1 and f2 files. The printf command is probably a better way to do that:

$ printf '%s %s %s\t%s\n' \
> 100644 blob 8e1e71d5ce34c01b6fe83bc5051545f2918c8c2b f1.txt \
> 100644 blob 9de77c18733ab8009a956c25e28c85fe203a17d7 f2.txt |
> git mktree
97da1d249d1b56762add1fb35096a46544416c7f
$ git checkout 97da1 -- .
$ ls
f1.txt  f2.txt
torek
  • 448,244
  • 59
  • 642
  • 775
  • yeah, I wanted to create a tree with two pointer: one to blob with the text `f1` and the other `f2` and then I wanted to check it out to have two files. So probably you're right I'm not passing the string correctly to `mktree`, but I tried you variant with two files in one string to printf "100644 blob 8e1e71d5ce3... 100644 blob 9de77c" and got the same error. – Max Koretskyi Aug 18 '17 at 19:18
  • I added an example using `printf`. – torek Aug 18 '17 at 20:15
  • thanks, working great. so what is the correct syntax the `mktree` expects for multiple objects? – Max Koretskyi Aug 19 '17 at 04:52
  • 1
    One line per entry (newline terminates entry), or one string per entry (NUL terminated) with `-z`, as [documented](https://www.kernel.org/pub/software/scm/git/docs/git-mktree.html). – torek Aug 19 '17 at 05:10
  • got it, thanks, actually the doc you linked doesn't explicitly define the syntax for multiple entries. just one more quick question - since commit references a tree and a branch references a commit I assume that git always eventually checks out a tree, correct? – Max Koretskyi Aug 19 '17 at 05:19
  • (Yes, it's implied/implicit: you have to read the `git ls-tree` documentation and/or output. Though "NUL terminated" itself implies multiple entries, each with a NUL after it.) `git checkout` will handle a tree, but only in the "check out tree contents" form. It sort of "prefers" commits, which in the `git checkout ` form become the *current* (HEAD) commit as a detached HEAD, or even-more-preferred, branch names, in the `git checkout ` form, which become the current branch as a non-detached ("attached"?) HEAD. – torek Aug 19 '17 at 05:28
  • Note that a branch name can always be resolved to a commit hash (try, e.g., `git rev-parse master`) and the commit itself contains a (as in *exactly one*) `tree` line (try `git cat-file -p master`). So given a valid branch name, that name become the current branch and the commit is its value; given a name that's not a valid branch name but which can become a hash ID, that hash ID becomes the current commit; in either case the corresponding `tree` object gets copied into the index and work-tree. – torek Aug 19 '17 at 05:31
  • got it, thanks a lot for you explanation, good luck! I also created [another question](https://unix.stackexchange.com/q/387105/90372) regarding `printf` that you used, if you have time take a look – Max Koretskyi Aug 19 '17 at 05:34