-
Notifications
You must be signed in to change notification settings - Fork 534
worktree: Commit method implementation #375
Conversation
Please, fix CI. |
Codecov Report
@@ Coverage Diff @@
## master #375 +/- ##
=========================================
- Coverage 77.37% 76.67% -0.7%
=========================================
Files 122 123 +1
Lines 8614 8751 +137
=========================================
+ Hits 6665 6710 +45
- Misses 1207 1295 +88
- Partials 742 746 +4
Continue to review full report at Codecov.
|
Solved, the problem was on master. |
options.go
Outdated
@@ -42,7 +43,7 @@ type CloneOptions struct { | |||
// Limit fetching to the specified number of commits. | |||
Depth int | |||
// RecurseSubmodules after the clone is created, initialize all submodules | |||
// within, using their default settings. This option is ignored if the | |||
// within, using their defaut settings. This option is ignored if the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why? 😆
options.go
Outdated
) | ||
|
||
// CommitOptions describes how a commit operation should be performed. | ||
type CommitOptions struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm missing the commit message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the commit message is at the method, the message is not a option
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think should be an option (https://git-scm.com/docs/git-commit#git-commit--mltmsggt), but it's depending of how we want to manage the public API.
} | ||
|
||
if head != nil { | ||
o.Parents = []plumbing.Hash{head.Hash()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If head is nil what happens?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that is the initial commit, and doesn't have any parents?
@@ -234,41 +234,40 @@ func (s *TagSuite) TestString(c *C) { | |||
) | |||
} | |||
|
|||
func (s *TagSuite) TestTagToTagString(c *C) { | |||
func (s *TagSuite) TestStringNonCommit(c *C) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand why this is related to this PR, and the name changing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was fixing the test because after changing the Write to auto-count the size, I figure out that the test was wrong.
- The test functions are called "Test[variant]"
- The test was wrong
- Didn't worked after autocalculate the size, because you can't use a custom hash in the objects. But since size was 0, the hash was not being calculated.
@@ -240,29 +240,21 @@ func (t *Tree) Encode(o plumbing.EncodedObject) error { | |||
return err | |||
} | |||
|
|||
var size int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is related to this commit, or is just a separated code improvement?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
worktree_commit.go
Outdated
return plumbing.ZeroHash, err | ||
} | ||
|
||
if opts.All == true { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
better do:
if opts.All {
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol
|
||
// Commit stores the current contents of the index in a new commit along with | ||
// a log message from the user describing the changes. | ||
func (w *Worktree) Commit(msg string, opts *CommitOptions) (plumbing.Hash, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the message is not part of the CommitOptions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I followed the pattern that we have in some other options. I can imaging someone reusing opts
, but never the message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find the design of the commit operation very surprising:
-
I would expect this method to receive what you want to commit (an index or whatever), along with the msg and the options.
-
I would expect this to be a method of Repository or Storage or any other entity, except for worktree.
Maybe you should document the overall design of this somewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you commit is what is in staging, is a very basic concept of git. If you want to do dirty things like storing an espontaneous commit just push is to the Storage.
Since you are committing the staging and this is a concept of the worktree, I don't see why this should be at Repository.
I think that this is very natural way of doing a commit:
r, err := Init(storage, fs)
w, err := r.Worktree()
billy.WriteFile(fs, "foo", []byte("foo"), 0644)
w.Add("foo")
w.Commit("foo\n", &CommitOptions{Author: defaultSignature()})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can have that if you want, with wrapper functions. But you don't actually need to write the core logic like that to get that usage pattern. The whole purpose of the index is to isolate the worktree from the repository:
-
there is a set of conceptual operations between the worktree and the index (like git add).
-
there is a different set of conceptual operations between the index and the repository (like git commit).
(except with a few concessions here and there due to pure usability that are always implemented at a very high level)
See, passing the index to the commit function avoids relaying on internal state and make it much easier to test and to mock. Then moving the commit function to a class that is not the worktree will be help to distribute responsibilities where they originally belong in the real git.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry but I am complete missing. Why I will want to make to the user pass the index, the index is a plumbing concept, not exposed on the git
package.
Mock and test is very easy because you can replace the storage where all is contained.
This is a small example of the jgit doing a commit with the porcelain API:
Git git = new Git(db);
CommitCommand commit = git.commit();
commit.setMessage("initial commit").call();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never mind, it is just a different point of view about the design.
These examples are very nice, maybe we can reuse them as actual real examples. They will help with the review of the PR and with the general documentation of the library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I pasted are not examples are the tests. As I said once that is merge I will do some examples
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would love to have these examples before the actual code, as a PR, so I can review them and understand the purposes of a change before reading the code.
// Validate validates the fields and sets the default values. | ||
func (o *CommitOptions) Validate(r *Repository) error { | ||
if o.Author == nil { | ||
return ErrMissingAuthor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can test it easily.
} | ||
|
||
if _, ok := h.entries[path]; ok { | ||
return false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can test it easily.
|
||
for path, fs := range s { | ||
if fs.Worktree != Modified && fs.Worktree != Deleted { | ||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can test it easily.
// a log message from the user describing the changes. | ||
func (w *Worktree) Commit(msg string, opts *CommitOptions) (plumbing.Hash, error) { | ||
if err := opts.Validate(w.r); err != nil { | ||
return plumbing.ZeroHash, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can test it easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand fully how a worktree works so I didn't understand this commit fully; not sure if when committing copies of files, duplicates appear in the repository objects. Is this kind of things handled by this code or tests anywhere?
options.go
Outdated
// Author is the author's signature of the commit. | ||
Author *object.Signature | ||
// Committer is the committer's signature of the commit. If Committer is | ||
// equal to nil the Author signature is used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove equal to
.
options.go
Outdated
// Committer is the committer's signature of the commit. If Committer is | ||
// equal to nil the Author signature is used. | ||
Committer *object.Signature | ||
// Parents parents commits for the new commit, by default is the hash of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add are the
between the repeated [P|p]arents
word.
Explain the meaning of by default
, is it what happens when Parents is nil or the empty slice or a slice with ZeroHashes inside?
(Personally, I recommend len(Parents) is zero
)
return err | ||
} | ||
|
||
if head != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clean the code with something like this:
if initialCommit := head != nil; initialCommit {
return nil
}
o.Parents = []plumbing.Hash{head.Hash()}
plumbing/object/commit.go
Outdated
@@ -30,10 +30,12 @@ type Commit struct { | |||
Committer Signature | |||
// Message is the commit message, contains arbitrary text. | |||
Message string | |||
// TreeHash hash of the tree pointed by the commit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this sentence is missing a verb, I suggest you add 'is the ' between the first and the second word.
pointed
is probably not the best word here, as it has a very specific meaning in programming, maybe just rewrite the full sentence:
TreeHash is the hash of the root tree of the commit.
plumbing/object/commit.go
Outdated
@@ -30,10 +30,12 @@ type Commit struct { | |||
Committer Signature | |||
// Message is the commit message, contains arbitrary text. | |||
Message string | |||
// TreeHash hash of the tree pointed by the commit. | |||
TreeHash plumbing.Hash | |||
// ParentHashes hashes of the parent commits of the commit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sentence is missing a verb, insert 'are the ' between the first two words.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find this too restrictive, I think commit should be an interface, but that's probably something to fix in another commit.
|
||
// Commit stores the current contents of the index in a new commit along with | ||
// a log message from the user describing the changes. | ||
func (w *Worktree) Commit(msg string, opts *CommitOptions) (plumbing.Hash, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find the design of the commit operation very surprising:
-
I would expect this method to receive what you want to commit (an index or whatever), along with the msg and the options.
-
I would expect this to be a method of Repository or Storage or any other entity, except for worktree.
Maybe you should document the overall design of this somewhere.
return plumbing.ZeroHash, err | ||
} | ||
|
||
return commit, w.updateHEAD(commit) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, explain that this method updates the head in the method signature, it is very important information about what the method does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you are going a commit? I don't know if we should explain the basic of git, here.
I can add an extra documentation. But could be an endless job.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, explaining all the things that a commit does is quite a lot.
Maybe the piece of information that is missing is that this method is supposed to emulate the porcelain git-commit command. It was not clear to me if this was a low-level operation or a high-level one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
almost all the operations in git package are porceleain ones.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And yet we don't explain that important fact anywhere in the package documentation.
Why should we not document that important feature of the package? why is it better to hide that information from the user?, why not clearly mention that on the functions that are meant to emulate git commands (or the opposite on the ones that are not, if they are minority)?
Our design decisions will be much easier to use and understand if we explain them, up front.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Explain what? We are not emulating the git command we are offering a easy way to manipulate a repository in the easy way. I invite you to document this if you want.
Is the main package and expose an API that is enough to manipulate a repository, the goal is not replicate a command, we are not doing this.
Doing a commit implies add to a repository the latest changes, explaining that this change the HEAD is a detail of implementation. You don't need to know how works internally to use it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a clarification, IMHO, we're not replicating git commands because translating commands to Go functions as they are would lead to a weird API. However, we do get a lot of inspiration from Git commands, both plumbing and porcelain, so that we work with the same concepts (even if it's with a different API) and they look familiar to advance Git users.
This is reflected on the fact that in some functions we documented what command they are equivalent or similar to.
worktree_commit.go
Outdated
} | ||
|
||
// commitIndexHelper converts a given index.Index file into multiple git objects | ||
// reading the blogs from the given filesystem and creating the trees from the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/blogs/blobs/
When to release this? |
This is the implementation of a basic
git commit
command.With the objective of create
Tree
objects thetree
andparents
were make public, the names are not the best ones, but I will create another PR improving this. (I didn't wanted to become this PR in a monster)Fixes #256