3802f2f270
Now that direct mode sets core.bare=true, git's normal prohibition about pushing into the currently checked out branch doesn't work. A simple fix for this would be an update hook which blocks the pushes.. but git hooks must be executable, and git-annex needs to be usable on eg, FAT, which lacks x bits. Instead, enabling direct mode switches the branch (eg master) to a special purpose branch (eg annex/direct/master). This branch is not pushed when syncing; instead any changes that git annex sync commits get written to master, and it's pushed (along with synced/master) to the remote. Note that initialization has been changed to always call setDirect, even if it's just setDirect False for indirect mode. This is needed because if the user has just cloned a direct mode repo, that nothing has synced with before, it may have no master branch, and only a annex/direct/master. Resulting in that branch being checked out locally too. Calling setDirect False for indirect mode moves back out of this branch, to a new master branch, and ensures that a manual "git push" doesn't push changes directly to the annex/direct/master of the remote. (It's possible that the user makes a commit w/o using git-annex and pushes it, but nothing I can do about that really.) This commit was sponsored by Jonathan Harrington.
105 lines
4.2 KiB
Markdown
105 lines
4.2 KiB
Markdown
Currently [[/direct_mode]] allows the user to point many normally safe
|
|
git commands at his foot and pull the trigger. At LCA2013, a git-annex
|
|
user suggested modifying direct mode to make this impossible.
|
|
|
|
One way to do it would be to move the .git directory. Instead, make there
|
|
be a .git-annex directory in direct mode repositories. git-annex would know
|
|
how to use it, and would be extended to support all known safe git
|
|
commands, passing parameters through, and in some cases verifying them.
|
|
|
|
So, for example, `git annex commit` would run `git commit --git-dir=.git-annex`
|
|
|
|
However, `git annex commit -a` would refuse to run, or even do something
|
|
intelligent that does not involve staging every direct mode file.
|
|
|
|
----
|
|
|
|
One source of problems here is that there is some overlap between git-annex
|
|
and git commands. Ie, `git annex add` cannot be a passthrough for `git
|
|
add`. The git wrapper could instead be another program, or it could be
|
|
something like `git annex git add`
|
|
|
|
--[[Joey]]
|
|
|
|
----
|
|
|
|
Or, no git wrapper could be provided. Limit the commands to only git-annex
|
|
commands. This should be all that is needed to manage a direct mode
|
|
repository simply, and if the user is doing something complicated that
|
|
needs git access, they can set `GIT_DIR=.git-annex` and be careful not to
|
|
shoot off their foot. (Or can just switch to indirect mode!)
|
|
|
|
This wins on simplicity, and if it's the wrong choice a git wrapper
|
|
can be added later. --[[Joey]]
|
|
|
|
---
|
|
|
|
Implementation: Pretty simple really. Already did the hard lifting to
|
|
support `GIT_DIR`, so only need to override the default git directory
|
|
in direct mode when that's not set to `.git-annex`.
|
|
|
|
A few things hardcode ".git", including Assistant.Threads.Watcher.ignored
|
|
and `Seek.withPathContents`, and parts of `Git.Construct`.
|
|
|
|
---
|
|
|
|
Transition: git-annex should detect when it's in a direct mode repository
|
|
with a .git directory and no .git-annex directory, and transparently
|
|
do the move to transition to the new scheme. (And remember that `git annex
|
|
indirect` needs to move it back.)
|
|
|
|
# alternative approach: move index
|
|
|
|
Rather than moving .git, maybe move .git/index?
|
|
|
|
This would cause git to think that all files in the tree were deleted.
|
|
So git commit -a would make a commit that removes them from git history.
|
|
But, the files in the work tree are not touched by this.
|
|
|
|
Also, git checkout, git merge, and other things that manipulate the work
|
|
tree refuse to do anything if they'd change a file that they think is
|
|
untracked.
|
|
|
|
Hmm, this does't solve the user accidentially running git add on an annexed
|
|
file; the whole file still gets added.
|
|
|
|
# alternative approach: fake bare repo
|
|
|
|
Set core.bare to true. This prevents all work tree operations,
|
|
so prevents any foot shooting. It still lets the user run commands like
|
|
git log, even on files in the tree, and git fetch, and push, and git
|
|
config, etc.
|
|
|
|
Even better, it integrates with other tools, like `mr`, so they know
|
|
it's a git repo.
|
|
|
|
This seems really promising. But of course, git-annex has its own set of
|
|
behaviors in a bare repo, so will need to recognise that this repo is not
|
|
really bare, and avoid them.
|
|
|
|
> [[done]]!! --[[Joey]]
|
|
|
|
(Git may also have some bare repo behaviors that are unwanted. One example
|
|
is that git allows pushes to the current branch in a bare repo,
|
|
even when `receive.denyCurrentBranch` is set.)
|
|
|
|
> This is indeed a problem. Indeed, `git annex sync` successfully
|
|
> pushes changes to the master branch of a fake bare direct mode repo.
|
|
>
|
|
> And then, syncing in the repo that was pushed to causes the changes
|
|
> that were pushed to the master branch to get reverted! This happens
|
|
> because sync commits; commit sees that files are staged in index
|
|
> differing from the (pushed) master, and commits the "changes"
|
|
> which revert it.
|
|
>
|
|
> Could fix this using an update hook, to reject the updated of the master
|
|
> branch. However, won't work on crippled filesystems! (No +x bit)
|
|
>
|
|
> Could make git annex sync detect this. It could reset the master
|
|
> branch to the last one committed, before committing. Seems very racy
|
|
> and hard to get right!
|
|
>
|
|
> Could make direct mode operate on a different branch, like
|
|
> `annex/direct/master` rather than `master`. Avoid pushing to that
|
|
> branch (`git annex sync` can map back from it to `master` and push there
|
|
> instead). A bit clumsy, but works.
|