On Thu, Aug 27, 2020 at 1:06 PM Junio C Hamano <gitster@xxxxxxxxx> wrote: > Eric Sunshine <sunshine@xxxxxxxxxxxxxx> writes: > > The .git file in a linked worktree is a "gitlink" which points back to > > Please never call it a "gitlink" (which is a word reserved for a > cache entry with 160000 mode that typically point at a commit > object, an implementation detail of the mechanism to bind a > submodule to its superproject) to avoid confusing future readers of > our code and documentation. The setup.c code calls it as "gitfile", > since it was introduced at b44ebb19 (Add platform-independent .git > "symlink", 2008-02-20), I think, and that is how it has been described > in the glossary. Thanks. I don't know why my brain latched onto "gitlink" even though I noted the difference in the name read_gitfile_gently(). > > the .git/worktrees/<id> entry in the main worktree or bare repository. > > If a worktree's .git file is deleted or becomes corrupted or outdated, > > then the linked worktree won't know how to find the repository or any of > > its own administrative files (such as 'index', 'HEAD', etc.). An easy > > way for the .git file to become outdated is for the user to move the > > main worktree or bare repository. > > I am not sure if a directory that used to be a secondary worktree > would still appear to be a Git controlled worktree after its ".git" > removed, but I guess a ".git" pointing at a wrong place won't, > either. The directory itself will be disconnected from its object database, thus non-working, however, its .git/worktrees/<id> entry will still be present, so the repository will still consider it a linked worktree. > I am just curious how much more involved in dealing with the "is > deleted" situation than "becomes corrupted" situation. They are handled identically. "git worktree repair" simply visits each .git/worktrees/<id> entry and checks if the /path/to/worktree/.git gitfile points back to .git/worktrees/<id>. If it doesn't -- whether because the gitfile is missing, corrupted, or simply pointing at the wrong location -- it recreates the gitfile with the correct content. > sequence, the user does > > user goes to a directory D, thinking it is a valid secondary > worktree and wanting to work in it, and finds that Git does not > think it is. > > user goes to X, which is the location of the primary copy, and > tells "worktree repair" that the directory D ought to be the top > of the working tree for a secondary worktree. > > "worktree repair" creates/updates D/.git gitfile to point at X. > > user comes back to D and starts working. > > because "git worktree repair" wants to do the usual setup sequence > anyway. Correct. Perhaps not 100% intuitive now that you mention it but that sort of interface could be built atop the machinery provided by this patch series, and I don't think this initial implementation needs to have all the bells and whistles. Even with what is implemented by this series, support becomes much easier -- simply ask the user to run "git worktree repair" in the main worktree (or in any functional worktree) -- than the situation presently which requires precise instructions about how to patch administrative files by hand. > > Move a working tree to a new location. Note that the main working tree > > -or linked working trees containing submodules cannot be moved. > > +or linked working trees containing submodules cannot be moved with this > > +command. (The `git worktree repair` command, however, can reestablish > > +the connection with linked working trees if you move the main working > > +tree manually.) > > ... meaning after moving the main working tree, repair can be used > to touch-up the submodule directories? > > Ah, no. You are saying "git worktree move" still cannot be used to > move the main working tree (the ones with submodules in it we do not > even care), but as a workaround, the user can "mv" it manually and > run "repair" to fix all the secondary worktrees. Seeing as the meaning wasn't clear upon your first reading, perhaps I should remove this change? Or rewrite it in some way? > Hopefully somewhere in the rest of this series it would become > automatic? After all, instead of letting users "mv" things without > us knowing what is going on, if we let them say "worktree move" the > primary working tree, we know both the source and the destination > directories of such a move, and because we know all the secondary > worktrees, we can run "worktree repair" on them as part of this > "worktree move" of the primary working tree. > > It is perfectly fine that it is not happening here in this step to > keep each step digestible, of course. Duy's original implementation did automatically repair worktree administrative files but it ended up being buggy (I don't recall the precise details) and discussion between the two of you resulted in the functionality being removed with the possibility of resurrecting it later. (Unfortunately, I can't find references to any of the discussion right now.) Automatic repair was definitely on my mind as I wrote this series, but as something which could possibly be added later atop the machinery implemented by this series. > > +test_corrupt_gitlink () { > > See gitglossary::gitfile. We'd need to find all references to > "gitlink" and "git link" in this series and decide which ones need > to be fixed (there might be genuine references to gitlink---I > haven't read the series through). They all need to be fixed. There aren't any genuine gitlink references in the series. > > +static void repair_dotgit(struct worktree *wt, > > + worktree_repair_cb *cb, void *cb_data) > > "dotgit" is an OK name in this context, I would think. repair_gitfile > is also fine, but "dotgit" may be more explicit. I think I originally called the function repair_gitlink() but changed it to "dotgit" to be more explicit. But now that I know "gitfile" is the correct term, repair_gitfile() seems preferable. > > + /* missing worktree can't be repaired */ > > + if (!file_exists(wt->path)) > > + return; > > Shouldn't it be a bit stronger? if wt->path is not a directory, it > cannot be the top of the working tree of a secondary worktree. I'm not quite sure what you're asking. This code is saying that if the path recorded in .git/worktrees/<id>/gitdir doesn't exist, then there simply isn't anything we can repair. > > + strbuf_realpath(&repo, git_common_path("worktrees/%s", wt->id), 1); > > OK, the need to use git_common_path() is a good justification why it > is easier to implement if we initialize ourselves using the primary > copy's repository data (i.e. require the user to start "worktree > repair" in the primary copy or any working secondary worktrees, and > let the normal setup.c sequence to prime us in the repository), > instead of allowing the user to start at the secondary worktree > whose gitfile got broken. Yes, and this does highlight that there are conditions under which "git worktree repair" simply won't operate. I did consider handling a number of such conditions but ultimately decided that I'd rather keep this initial implementation as simple and straightforward as possible. Such improvements can be built atop. > > + if (repair) { > > + cb(0, wt->path, repair, cb_data); > > + write_file(dotgit.buf, "gitdir: %s", repo.buf); > > Shouldn't write_file() be monitored for its failure, and the failure > reported back to the callback function? Yes and no. write_file() will die() if it can't write the file. That's not necessarily ideal behavior for a command which wants to repair _all_ problems, and the decision to use write_file() was not one I made lightly. The bigger picture is that, although "git worktree repair" ideally should not abort when attempting repairs, there are a number of library functions called by "git worktree repair" which undesirably die(). And writing all that functionality from scratch or enhancing those functions to have "gentle" modes would have made this series far longer and far more complex. So, I eventually decided to keep the implementation simple, with the idea that such enhancements can be made later as needed and as we gain experience with the command. > > +void repair_worktrees(worktree_repair_cb *cb, void *cb_data) > > +{ > > + struct worktree **worktrees = get_worktrees(); > > + struct worktree **wt = worktrees + 1; /* +1 skips main worktree */ > > + > > + if (!cb) > > + cb = repair_noop_cb; > > + for (; *wt; wt++) > > + repair_dotgit(*wt, cb, cb_data); > > + free_worktrees(worktrees); > > +} > > This "repair"s only in one direction. Manual movement of secondary > worktrees, if we want to support repairing, needs the user to tell > where the new location of the secondary is, and we need a code to > update the record of the location of the secondary kept at the main > worktree, which is not needed in the repair implemented by this > step. Correct. Patch [3/5] implements repair in the other direction. > Looking good. Thanks for the thoughtful review comments (including the ones to which I didn't respond directly -- they will be addressed by the re-roll itself).