From 15d3af5e22eceb9cd6cd341703d612a62194451e Mon Sep 17 00:00:00 2001 From: Jiang Xin Date: Thu, 27 Aug 2020 11:45:44 -0400 Subject: receive-pack: add new proc-receive hook Git calls an internal `execute_commands` function to handle commands sent from client to `git-receive-pack`. Regardless of what references the user pushes, git creates or updates the corresponding references if the user has write-permission. A contributor who has no write-permission, cannot push to the repository directly. So, the contributor has to write commits to an alternate location, and sends pull request by emails or by other ways. We call this workflow as a distributed workflow. It would be more convenient to work in a centralized workflow like what Gerrit provided for some cases. For example, a read-only user who cannot push to a branch directly can run the following `git push` command to push commits to a pseudo reference (has a prefix "refs/for/", not "refs/heads/") to create a code review. git push origin \ HEAD:refs/for// The `` in the above example can be as simple as "master", or a more complicated branch name like "foo/bar". The `` in the above example command can be the local branch name of the client side, such as "my/topic". We cannot implement a centralized workflow elegantly by using "pre-receive" + "post-receive", because Git will call the internal function "execute_commands" to create references (even the special pseudo reference) between these two hooks. Even though we can delete the temporarily created pseudo reference via the "post-receive" hook, having a temporary reference is not safe for concurrent pushes. So, add a filter and a new handler to support this kind of workflow. The filter will check the prefix of the reference name, and if the command has a special reference name, the filter will turn a specific field (`run_proc_receive`) on for the command. Commands with this filed turned on will be executed by a new handler (a hook named "proc-receive") instead of the internal `execute_commands` function. We can use this "proc-receive" command to create pull requests or send emails for code review. Suggested by Junio, this "proc-receive" hook reads the commands, push-options (optional), and send result using a protocol in pkt-line format. In the following example, the letter "S" stands for "receive-pack" and letter "H" stands for the hook. # Version and features negotiation. S: PKT-LINE(version=1\0push-options atomic...) S: flush-pkt H: PKT-LINE(version=1\0push-options...) H: flush-pkt # Send commands from server to the hook. S: PKT-LINE( ) S: ... ... S: flush-pkt # Send push-options only if the 'push-options' feature is enabled. S: PKT-LINE(push-option) S: ... ... S: flush-pkt # Receive result from the hook. # OK, run this command successfully. H: PKT-LINE(ok ) # NO, I reject it. H: PKT-LINE(ng ) # Fall through, let 'receive-pack' to execute it. H: PKT-LINE(ok ) H: PKT-LINE(option fall-through) # OK, but has an alternate reference. The alternate reference name # and other status can be given in options H: PKT-LINE(ok ) H: PKT-LINE(option refname ) H: PKT-LINE(option old-oid ) H: PKT-LINE(option new-oid ) H: PKT-LINE(option forced-update) H: ... ... H: flush-pkt After receiving a command, the hook will execute the command, and may create/update different reference. For example, a command for a pseudo reference "refs/for/master/topic" may create/update different reference such as "refs/pull/123/head". The alternate reference name and other status are given in option lines. The list of commands returned from "proc-receive" will replace the relevant commands that are sent from user to "receive-pack", and "receive-pack" will continue to run the "execute_commands" function and other routines. Finally, the result of the execution of these commands will be reported to end user. The reporting function from "receive-pack" to "send-pack" will be extended in latter commit just like what the "proc-receive" hook reports to "receive-pack". Signed-off-by: Jiang Xin Signed-off-by: Junio C Hamano --- remote.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'remote.h') diff --git a/remote.h b/remote.h index 11d8719b58..ca7735b441 100644 --- a/remote.h +++ b/remote.h @@ -93,6 +93,14 @@ int for_each_remote(each_remote_fn fn, void *priv); int remote_has_url(struct remote *remote, const char *url); +struct ref_push_report { + const char *ref_name; + struct object_id *old_oid; + struct object_id *new_oid; + unsigned int forced_update:1; + struct ref_push_report *next; +}; + struct ref { struct ref *next; struct object_id old_oid; -- cgit v1.2.3 From 63518a574a3a4b8c6f152b29cb1b6d1f66f6e6c7 Mon Sep 17 00:00:00 2001 From: Jiang Xin Date: Thu, 27 Aug 2020 11:45:46 -0400 Subject: New capability "report-status-v2" for git-push The new introduced "proc-receive" hook may handle a command for a pseudo-reference with a zero-old as its old-oid, while the hook may create or update a reference with different name, different new-oid, and different old-oid (the reference may exist already with a non-zero old-oid). Current "report-status" protocol cannot report the status for such reference rewrite. Add new capability "report-status-v2" and new report protocol which is not backward compatible for report of git-push. If a user pushes to a pseudo-reference "refs/for/master/topic", and "receive-pack" creates two new references "refs/changes/23/123/1" and "refs/changes/24/124/1", for client without the knowledge of "report-status-v2", "receive-pack" will only send "ok/ng" directives in the report, such as: ok ref/for/master/topic But for client which has the knowledge of "report-status-v2", "receive-pack" will use "option" directives to report more attributes for the reference given by the above "ok/ng" directive. ok refs/for/master/topic option refname refs/changes/23/123/1 option new-oid ok refs/for/master/topic option refname refs/changes/24/124/1 option new-oid The client will report two new created references to the end user. Suggested-by: Junio C Hamano Suggested-by: Jeff King Signed-off-by: Jiang Xin Signed-off-by: Junio C Hamano --- remote.h | 1 + 1 file changed, 1 insertion(+) (limited to 'remote.h') diff --git a/remote.h b/remote.h index ca7735b441..6d1e5c49f8 100644 --- a/remote.h +++ b/remote.h @@ -148,6 +148,7 @@ struct ref { REF_STATUS_ATOMIC_PUSH_FAILED } status; char *remote_status; + struct ref_push_report *report; struct ref *peer_ref; /* when renaming */ char name[FLEX_ARRAY]; /* more */ }; -- cgit v1.2.3