git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Subprojects tasks

From
Junio C Hamano <junkio@cox.net>
Date
Dec 16, 2006, 18:32 UTC
Message-ID
<7vzm9nelob.fsf@assigned-by-dhcp.cox.net>

Because I am primarily a plumber, I was thinking about the changes that need to be done at the plumbing level. I only looked at the prototype when it was announced, and I do not know the progress you made since then. Could you tell us the current status?

I am assuming that the overall design is based on what Linus proposed long time ago with his "gitlink" object. That is,

 * the index and the tree object for the superproject has a
   "link" object that tells there is a directory and the
   corresponding commit object name from the subproject.  Unlike
   my previous "bind commit" based prototype, index does not
   have any blobs nor trees from the subproject.
 * the subproject is on its own, and can exist unaware of the
   existence of its superproject (there is no back-link at the
   object layer).
 * the subproject and the superproject are loosely coupled.  An
   act of committing in one does not automatically make a
   corresponding commit in the other at the plumbing level.

For now, I assume that the representation of the "link" object is (after the usual object header) 40-byte hexadecimal SHA-1 of the commit object, plus a LF, but that is a minor detail.

At the object layer, obviously you would need a new object type allocated, "link", and the mode-bits assigned in the tree object. You should be able to cover cat-file, fsck-objects (connectivity through link->commit), pack-objects and unpack-objects (they need to know about the new object type) at that stage.

With the index, in addition to the mode bits and "link" object's SHA-1, you would need to decide what to do with ce_match_stat(), to keep track of the information "update-index --refresh" updates. My recommendation is to:

 (1) Look at the directory the "link" is at, and find .git/
     subdirectory (that is the $GIT_DIR for the subproject) and
     its .git/HEAD;
 (2) If that points at a loose ref, use the file's stat()
     information (e.g stat("$sub/.git/refs/heads/master"));
 (3) Otherwise, use the packed-ref file's stat() information
     (e.g stat("$sub/.git/packed-refs")).

Then ce_match_stat() for a "link" entry can do the same computation and tell if the subproject has changed its HEAD.

I think "update-index --add $directory" should check if .git/ exists and looks like a valid repository, and make a "link" object out of "$directory/.git/HEAD".

Another issue with the index is what to put in the cache_tree structure; I think "link" can be treated just like blob (both files and symlinks).

Then read-tree (bulk of it is in unpack-trees.c) needs to be taught to read in "link" and put that into the index -- this should be straightforward.

After you have a working index, you should be able to do write-tree (writes the new "link" entry as is, without descending into the subproject) trivially.

It is debatable what 'checkout-index -f' should do when the subproject is already checked out and its HEAD points at a different commit. I am tempted to say that it should go there and run "reset --hard", but I feel uneasy about that because it is a blatant layering violation. Maybe it should simply ignore link entries and let the Porcelain layer take care of them.

Then there are three diff- brothers at the plumbing level. I think it is reasonable not to make them recurse into "link", even with the presense of -r (recursive) option (Porcelain "git diff" might want to recurse into the subproject, perhaps with a new --recurse-harder option, though).

That means diff-files either skips a "link" entry if ce_match_stat() says it is clean, or feeds "link" and its recorded SHA-1 from the index on the left hand side, and 0{40} on the right hand side with "link" type (after verifying that "$sub/.git" is still there -- otherwise you would say that the working tree has lost that subproject). "Read from the working tree" done for diff-files for a "link" object would grab the commit SHA-1 from the tip of the current branch of the subproject, format it as the value of a "link" object (I am assuming just a 40-byte commit SHA-1, plus a LF) and would compare that with the result of read_sha1_file() on the link object recorded in the index when producing -p (patch) output.

diff-tree would compare "link" entries without descending into them. "link" and "blob" would compare just like "symlink" and "blob" would compare.

diff-index with --cached would work like diff-tree (two concrete SHA-1), and without --cached would work like diff-files (one SHA-1 from the tree, another is either from the index if ce_match_stat() says it is clean, 0{40} otherwise).

I suspect the hardest part is "rev-list --objects" (now most of it is found in revision.c). Theoretically, if the code can handle "tag"s, it should be able to handle "link"s, but I have a feeling that the ancestry traversal code that walks commits is not prepared to see "commit" object to appear from somewhere in the middle of traversal. A commit so far can be wrapped only by tags zero or more times, and a tag never appears inside anything but another tag, so the code can just keep peeling the tag until it sees a non-tag and after that it will be living in the world that has only commit->tree->blob hierarchy, and can afford to do the ancestry based solely on "commit" and can treat reachability for "tree" and "blob" as afterthought. But I think the updated code needs to know that "link" needs to be unwrapped and contained "commit" needs to be injected back to the ancestry walking machinery.

Once you have "rev-list --objects", you should be able to drive pack-objects with its output. I do not think there is much to change in that program.

My gut feeling is that it would take about 2-3 weeks for a competent plumber working on full time to make the above changes to the plumbing side into presentable shape.

On the Porcelain side, you would need policy design, some of which were discussed on the list, such as what committing and fetching in a superproject mean and should do. I do not have a guestimate of the amount of work that would involve.

Next: Jakub Narebski
Message 1 of 24 in “Subprojects tasks”
  1. Junio C HamanoDec 16, 2006
  2. Jakub NarebskiDec 16, 2006
  3. Martin WaitzDec 16, 2006
  4. Jakub NarebskiDec 16, 2006
  5. Josef WeidendorferDec 17, 2006
  6. Martin WaitzDec 17, 2006
  7. Jakub NarebskiDec 17, 2006
  8. Martin WaitzDec 17, 2006
  9. Jakub NarebskiDec 17, 2006
  10. Martin WaitzDec 17, 2006
  11. Josef WeidendorferDec 17, 2006
  12. Martin WaitzDec 18, 2006
  13. Josef WeidendorferDec 17, 2006
  14. Martin WaitzDec 18, 2006
  15. Josef WeidendorferDec 18, 2006
  16. Josef WeidendorferDec 17, 2006
  17. Sven VerdoolaegeDec 16, 2006
  18. Junio C HamanoDec 16, 2006
  19. Martin WaitzDec 16, 2006
  20. Sven VerdoolaegeDec 16, 2006
  21. Josef WeidendorferDec 17, 2006
  22. Alan ChandlerDec 17, 2006
  23. Jakub NarebskiDec 17, 2006
  24. Martin WaitzDec 17, 2006

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.