commit - a0275e4081617f9d84038b640e26fd0af5bbfb49
commit + 09f5bd908b0b901f2123f462e4800e76a3d1b10e
blob - 52d7a09962ece61d245acc31d840a4b52ab3d012
blob + 640ad150c74800a6bd02833ddf62dbb7d2c07004
--- include/got_error.h
+++ include/got_error.h
#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;
{ 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
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;
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;
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;
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;
}