Until recently, there were no git clients for Plan 9. Upstream git is large, included perl and bash code, and used a large number of system calls that Plan 9 does not support. In addition, it does not feel good to use, with a bewildering array of inconsistent command line arguments and ad-hoc commands.
The file formats and wire formats for git make …interesting.. design choices, but they aren’t particularly hard to implement, so I followed the documentation and implemented them.
Now, we have a native Plan 9 git implementation. It presents read only access to scripts via a file system mounted on /mnt/git, and manipulates the repository directly. The ability to access the repository directly from the shell using standard tools gives this implementation a very plan9-y feel.
There’s more care given to a consistent and minimalist interface that supports the necessary functionality, which will hopefully lead to a more pleasant user experience. Commands like checkout
will not be overloaded with all functionality under the sun.
For an extra dose of irony, it’s currently hosted in mercurial, since that’s the current ‘native’ plan 9 version control system. Eventually, it will be developed using itself, but it needs to be beaten on more before I’m confident.
Enjoy!
This is actually kind of cool!
Did you consider using libgit2 in your approach? Why did it not fit your needs?
Porting is annoying, and ape (the ansi/posix emulation environment) is a separate world that’s no fun to use. It also comes with interoperability caveats and issues when using the native plan9 libraries. Additionally, the things that libgit2 helps with are the things I want to do in shell script. And finally:
Compared to:
Even ignoring tests, examples, and external deps:
In other words, depending on how you count, there’s between 1.2% and 2.4% the amount of C code to maintain, update, and understand in this implementation. And sure, this implementation will grow, but it’s likely to stay within the single digit percentages of libgit2’s size.
Thank you for your response!
But what about adding parts of files to commits?
As in
git add -p
for Interactive Staging?You can try working on one thing at a time, or you can leverage your favourite editor’s functionality, if possible. VIM (and I bet many others) can do partial writes to files, so if you keep touching many areas but want to stage/commit them one by one, write the specific areas only and proceed as usual.
Developers have been surviving without those bells and whistles for a long time. I do use it now and then when things got too complicated, but that’s not (necessarily) a good thing: I wish I never run into this in the first place.
Yeah, but now we can use -i and -p, we don’t want to go back. But partial writes may be a good answer, I didn’t know about those.
Adding parts of files to commits is fragile, because by definition you’ve never tested those staged files as a unit, and it’s quite easy to break the code as a result.
I’d strongly prefer a partial stash, similar to
git stash -p
. That gives you a checkout that you can test before committing. Plan 9 already comes with tools for doing this[1], and the file system abstraction means that the only support needed is for hooking it into a stash workflow.[1]
idiff
, http://man.cat-v.org/9front/1/idiffOh, I “want to go back” on so many things in git…
I love git, seriously. Now that I know I’m manipulating the DAG, everything just Makes Sense. I wouldn’t want to teach a new user though.
After
git/add foo.c
, which state isfoo.c
: “dirty” or “committed”? It’s not committed and not untracked, so I guess it must be “dirty”. But if it’s again modified in the workspace… then what?Seems to me like there is still a “staging” step here, or please clarify.
Naively snapshotting an entire directory will pick up in-progress files, generated outputs, and other undesirable stragglers. We need a method to mark files that should be included in a commit.
git/add
is the tool for this. It does not copy files anywhere, it just marks a file as snapshot-worthy.git/commit
will snapshot whatever is in the working directory at the time you call it. The concept of a staging area is simply gone. This reduces the amount of state I need to track in my head, improving usability. (See, for example, the usability studies done by the gitless folks: https://spderosso.github.io/oopsla16.pdf, https://gitless.com/)One major missing feature that results from this decision is partial commits. This will be supported by explicitly listing the files to snapshot when committing, as in
git/commit path/to/file
. That will work by creating a fresh namespace from /mnt/git, and binding in just the files passed as dirty. Something like:(Edited for clarity)
I can appreciate the simplicity, but then why bother with
git/add
at all? Why notgit/commit
everything always (except.gitignore
’d files)?Removing the “staging” phase implies that if one wants to divide changes into separate commits, one must find some other mechanism or discipline. But keeping the
git/add
step means that the opt-in habit is still required, except with less granularity. Going full opt-out (.gitignore
) eliminatesgit/add
(except when needed to override.gitignore
), and eliminates “untracked” (because one must update.gitignore
to avoid committing such files). Untracked-unignored files are a bad habit anyway.Replying to your edit:
That is still conceptually (“head state”) a staging phase, except at file-granularity instead of chunk-granularity. Going “full opt-out” would fully eliminate the staging phase.
It’s an interesting thought. Not sure how I feel about it, but I’ll experiment and see how it works in practice.
This idea mostly works, but does require a feature that I don’t think the Plan 9 implementation has, which is easy per-checkout ignores (i.e., the equivalent of
.git/info/exclude
). Otherwise, it’s pretty easy to get into a situation where you constantly need to move temp files (e.g., log output from a bug you’re trying to fix) out of the repo before the commit and then move them back in, which gets old fast.I don’t see why something like
.git/info/exclude
would not be supported (the currentgit/add
behavior already implies support for some sort of local tracking list), but why not add such “temp files” to.gitignore
?In any case you’re going to run into an analogous situation if you don’t want to commit certain modifications to your workspace. This is the “other mechanism or discipline” I mentioned above.
I like the concept, removing a feature and leveraging existing features.
You sounds like a git wizard. I guess you may want to take a look at rcs, cvs, svn, and hg.
Looks like it’s MIT-licensed. I wonder if freebsd might adopt git, then.
I needed that.