commit 09f5bd908b0b901f2123f462e4800e76a3d1b10e from: Stefan Sperling date: Thu May 09 19:36:10 2019 UTC try to detect concurrent commits; not perfect yet (see comments) commit - a0275e4081617f9d84038b640e26fd0af5bbfb49 commit + 09f5bd908b0b901f2123f462e4800e76a3d1b10e blob - 52d7a09962ece61d245acc31d840a4b52ab3d012 blob + 640ad150c74800a6bd02833ddf62dbb7d2c07004 --- include/got_error.h +++ include/got_error.h @@ -85,6 +85,7 @@ #define GOT_ERR_COMMIT_CONFLICT 69 #define GOT_ERR_BAD_REF_TYPE 70 #define GOT_ERR_COMMIT_NO_AUTHOR 71 +#define GOT_ERR_COMMIT_HEAD_CHANGED 72 static const struct got_error { int code; @@ -161,6 +162,8 @@ static const struct got_error { { GOT_ERR_COMMIT_CONFLICT,"cannot commit file in conflicted status" }, { GOT_ERR_BAD_REF_TYPE, "bad reference type" }, { GOT_ERR_COMMIT_NO_AUTHOR,"GOT_AUTHOR environment variable is not set" }, + { GOT_ERR_COMMIT_HEAD_CHANGED, "branch head in repository has changed " + "while commit was in progress" }, }; /* blob - e384ebc955c80bfb8377f98004ea75fd12568b56 blob + 45237270ff29274f7f93126f03f903dc7e87bbea --- lib/worktree.c +++ lib/worktree.c @@ -2738,8 +2738,11 @@ got_worktree_commit(struct got_object_id **new_commit_ struct collect_commitables_arg cc_arg; struct got_pathlist_head commitable_paths; struct got_pathlist_entry *pe; - char *relpath = NULL; + char *relpath = NULL, *head_ref_str = NULL; struct got_commit_object *base_commit = NULL; + struct got_object_id *head_commit_id = NULL; + struct got_reference *head_ref2 = NULL; + struct got_object_id *head_commit_id2 = NULL; struct got_tree_object *base_tree = NULL; struct got_object_id *new_tree_id = NULL; struct got_object_id_queue parent_ids; @@ -2761,6 +2764,11 @@ got_worktree_commit(struct got_object_id **new_commit_ if (err) goto done; + /* XXX should re-read head ref here now that work tree is locked */ + err = got_ref_resolve(&head_commit_id, repo, worktree->head_ref); + if (err) + goto done; + cc_arg.commitable_paths = &commitable_paths; cc_arg.worktree = worktree; cc_arg.repo = repo; @@ -2817,17 +2825,36 @@ got_worktree_commit(struct got_object_id **new_commit_ if (err) goto done; - err = got_worktree_set_base_commit_id(worktree, repo, *new_commit_id); + /* Check if a concurrent commit to our branch has occurred. */ + /* XXX ideally we'd lock the reference file here to avoid a race */ + head_ref_str = got_ref_to_str(worktree->head_ref); + if (head_ref_str == NULL) { + err = got_error_from_errno(); + goto done; + } + err = got_ref_open(&head_ref2, repo, head_ref_str); + if (err) + goto done; + err = got_ref_resolve(&head_commit_id2, repo, head_ref2); if (err) goto done; - + if (got_object_id_cmp(head_commit_id, head_commit_id2) != 0) { + err = got_error(GOT_ERR_COMMIT_HEAD_CHANGED); + goto done; + } + /* Update branch head in repository. */ err = got_ref_change_ref(worktree->head_ref, *new_commit_id); if (err) goto done; err = got_ref_write(worktree->head_ref, repo); if (err) goto done; + /* XXX race has ended here */ + err = got_worktree_set_base_commit_id(worktree, repo, *new_commit_id); + if (err) + goto done; + err = ref_base_commit(worktree, repo); if (err) goto done; @@ -2850,5 +2877,10 @@ done: if (base_commit) got_object_commit_close(base_commit); free(relpath); + free(head_commit_id); + free(head_commit_id2); + free(head_ref_str); + if (head_ref2) + got_ref_close(head_ref2); return err; }