Many git forges (gitlab, gitea, forgejo, etc.) support push-to-create to create repositories upon the first push, e.g.
# add the (still nonexistant) remote to this local repo
> git remote add myserver ssh://me@myserver.com/me/myrepo
# push to the remote as if it existed (will create it on the remote)
> git push -u myserver
This is very useful as it enables quick creation of repos without going through a tedious GUI.
However, git annex assist|sync|push
seem to push git-annex
, synced/git-annex
, or synced/<currentbranch>
(in a seemingly random order? π€) before pushing <currentbranch>
itself, causing this first pushed branch to become the repository's default branch. A git clone ssh://me@myserver.com/me/myrepo
will then result in a local repo with e.g. synced/main
checked out - or worse - synced/git-annex
, causing a lot of confusion. Accidentally running git annex assist
again will produce another level of synced/synced/main
branches and all that fun stuff. (Very fun time during that summer school where I established git-annex + forgejo as data exchange π).
Of course the solution is to just git push
manually before git annex assist
. But git annex assist
is already such a brilliant command that does it all, and telling people to just run that to "do the git stuff" is very comfortable and easily accepted. Could the current branch be pushed first? Or is there a reason for pushing all the meta-branches first?
Command/Sync.hs has a big comment on pushBranch about push order considerations. Basically:
git-annex sync
, and that complaint would be unwanted noise. git progress output also goes to stderr, so /dev/null of stderr is not desirable.Also this was previously considered and partly addressed in 1cc7b2661e5ec60f73f04dbe91940d2602df6246 which made it push synced/master before synced/git-annex, to at least avoid the git-annex branch becoming the default branch. The varying behavior you're seeing may be due to using a version from before that change. At that point I thought this was a github specific problem, mind.
I think that to improve this, git-annex would need to run git push of master with stderr intercepted and the denyCurrentBranch error message filtered out, but the rest of stderr (progress, etc) still displayed. Which does seem doable.
Doable, but not easy! The message that would need to be filtered:
Filtering out the error part of that while keeping the rest, and without assuming too much about git's output, would be rather hard. Especially since progress output is displayed too, so it has to decide whether or not to display the first line of that error message, before it has necessarily received the rest of it. So it can't look for "receive.denyCurrentBranch" to know when to filter out that error message.
Hmm, the 'refuse' setting seems to have been added after git-annex dealt with this, and setting that does avoid the ugly message, and users could just be left to set it if they dislike it. But that would be disruptive for existing users, and a lot of bother for what is a pretty common use case for git-annex.
I came up with the following patch before realizing this difficulty. So its filterstderr is wrong.
If the remote is brand-new, there will be no remote tracking refs. git-annex could detect that as a special case, and push the current branch first then. Since that would only be done one time, the user would only see any receive.denyCurrentBranch message once. So it wouldn't be appreciable clutter.
And in the common case where a user has cloned a repo to another drive, there would already be a tracking remote ref, so the special case wouldn't happen. And that's the kind of case where a clone of a non-bare repository is typically done.
This approach does have an edge case where the wrong thing is done: If the user had origin pointing to one repository, but then changed the url to a new, empty repository, sync would do the usual push, not the special case. And so if the new origin url was github or whatever has this problem, they would still end up pushing the "wrong" branch first.
That is an unlikely edge case, but it's also the kind of edge case that makes it hard to reason about software, IMHO.
For filtering, the best approach I can see to do that is to look for lines starting with "remote:", and buffer blocks of those lines. At the end of a "remote:" block, look for "receive.denyCurrentBranch" in it to decide whether to filter it out or display it. Pass other lines through.
Note that the remote may choose to display anything at all to stderr, and it would probably all come in the same block, so that might filter out messages that the user legitimatly would expect to see. But I suppose in that case, the other git push that is run for the sync branches, which does not get filtered, would still let such messages be displayed.
Currently, git does not localize the "remote:" even when the message itself is localized. This might just be bad/missing localization; some other parts also don't get localized:
I suppose if git localizes "remote:" later, it will only prevent the filtering, so not the end of the world.
Implemented this.
Thank you very much joey, I can confirm that the current branch is now pushed first and thus used as the default branch of the newly created repo:
New version
Old version