commit 123836732402b1204e5be68f98a69084285c316d from: Mark Jamsek date: Sat Feb 18 16:32:32 2023 UTC add ci/he/mg/rb -C option to commit unresolved conflicts As per stsp's suggestion and building on his initial diff, add the -C option to enable creating commits with unresolved conflicts to the commit, histedit, merge, and rebase commands to allow continuing the operation despite files in conflict status. Also, only search for conflict markers in newly added lines to enable working with files already under version control that may have conflict markers embedded verbatim. lots of tweaks, improvements, and initial diff + ok stsp@ commit - f990756a3987ba6410baf611d561e9b8f285f047 commit + 123836732402b1204e5be68f98a69084285c316d blob - bb1825eecded3623bbabcf0541bce55367696327 blob + b6cc16e8e5a4b02bf33a4f2af21bc0fd0f58c4bd --- got/got.1 +++ got/got.1 @@ -1652,7 +1652,7 @@ is a directory. .Tg ci .It Xo .Cm commit -.Op Fl NnS +.Op Fl CNnS .Op Fl A Ar author .Op Fl F Ar path .Op Fl m Ar message @@ -1740,6 +1740,14 @@ information which is obtained, as usual, from the environment variable, or .Xr got.conf 5 , or Git configuration settings. +.It Fl C +Allow committing files in conflicted status. +.Pp +Committing files with conflict markers should generally be avoided. +Cases where conflict markers must be stored in the repository for +some legitimate reason should be very rare. +There are usually ways to avoid storing conflict markers verbatim by +applying appropriate programming tricks. .It Fl F Ar path Use the prepared log message stored in the file found at .Ar path @@ -2209,7 +2217,7 @@ This option cannot be used with .Tg rb .It Xo .Cm rebase -.Op Fl aclX +.Op Fl aCclX .Op Ar branch .Xc .Dl Pq alias: Cm rb @@ -2359,6 +2367,11 @@ are as follows: .It Fl a Abort an interrupted rebase operation. If this option is used, no other command-line arguments are allowed. +.It Fl C +Allow a rebase operation to continue with files in conflicted status. +This option should generally be avoided, and can only be used with the +.Fl c +option. .It Fl c Continue an interrupted rebase operation. If this option is used, no other command-line arguments are allowed. @@ -2422,7 +2435,7 @@ None of the other options can be used together with .Tg he .It Xo .Cm histedit -.Op Fl acdeflmX +.Op Fl aCcdeflmX .Op Fl F Ar histedit-script .Op Ar branch .Xc @@ -2597,6 +2610,11 @@ are as follows: .It Fl a Abort an interrupted histedit operation. If this option is used, no other command-line arguments are allowed. +.It Fl C +Allow a histedit operation to continue with files in conflicted status. +This option should generally be avoided, and can only be used with the +.Fl c +option. .It Fl c Continue an interrupted histedit operation. If this option is used, no other command-line arguments are allowed. @@ -2749,7 +2767,7 @@ or reverted with .Tg mg .It Xo .Cm merge -.Op Fl acn +.Op Fl aCcn .Op Ar branch .Xc .Dl Pq alias: Cm mg @@ -2864,6 +2882,11 @@ are as follows: .It Fl a Abort an interrupted merge operation. If this option is used, no other command-line arguments are allowed. +.It Fl C +Allow a merge operation to continue with files in conflicted status. +This option should generally be avoided, and can only be used with the +.Fl c +option. .It Fl c Continue an interrupted merge operation. If this option is used, no other command-line arguments are allowed. blob - 97e8fcfb43b02a9fb38a2a41964a9a6249b31c2b blob + 438e15cb2486d96840625d284c94c90018d7917b --- got/got.c +++ got/got.c @@ -8776,7 +8776,7 @@ done: __dead static void usage_commit(void) { - fprintf(stderr, "usage: %s commit [-NnS] [-A author] [-F path] " + fprintf(stderr, "usage: %s commit [-CNnS] [-A author] [-F path] " "[-m message] [path ...]\n", getprogname()); exit(1); } @@ -9083,7 +9083,7 @@ cmd_commit(int argc, char *argv[]) char *gitconfig_path = NULL, *editor = NULL, *committer = NULL; int ch, rebase_in_progress, histedit_in_progress, preserve_logmsg = 0; int allow_bad_symlinks = 0, non_interactive = 0, merge_in_progress = 0; - int show_diff = 1; + int show_diff = 1, commit_conflicts = 0; struct got_pathlist_head paths; struct got_reflist_head refs; struct got_reflist_entry *re; @@ -9099,7 +9099,7 @@ cmd_commit(int argc, char *argv[]) err(1, "pledge"); #endif - while ((ch = getopt(argc, argv, "A:F:m:NnS")) != -1) { + while ((ch = getopt(argc, argv, "A:CF:m:NnS")) != -1) { switch (ch) { case 'A': author = optarg; @@ -9107,6 +9107,9 @@ cmd_commit(int argc, char *argv[]) if (error) return error; break; + case 'C': + commit_conflicts = 1; + break; case 'F': if (logmsg != NULL) option_conflict('F', 'm'); @@ -9230,8 +9233,8 @@ cmd_commit(int argc, char *argv[]) } cl_arg.repo_path = got_repo_get_path(repo); error = got_worktree_commit(&id, worktree, &paths, author, committer, - allow_bad_symlinks, show_diff, collect_commit_logmsg, &cl_arg, - print_status, NULL, repo); + allow_bad_symlinks, show_diff, commit_conflicts, + collect_commit_logmsg, &cl_arg, print_status, NULL, repo); if (error) { if (error->code != GOT_ERR_COMMIT_MSG_EMPTY && cl_arg.logmsg_path != NULL) @@ -10368,7 +10371,7 @@ done: __dead static void usage_rebase(void) { - fprintf(stderr, "usage: %s rebase [-aclX] [branch]\n", getprogname()); + fprintf(stderr, "usage: %s rebase [-aCclX] [branch]\n", getprogname()); exit(1); } @@ -10492,7 +10495,8 @@ static const struct got_error * rebase_commit(struct got_pathlist_head *merged_paths, struct got_worktree *worktree, struct got_fileindex *fileindex, struct got_reference *tmp_branch, const char *committer, - struct got_object_id *commit_id, struct got_repository *repo) + struct got_object_id *commit_id, int allow_conflict, + struct got_repository *repo) { const struct got_error *error; struct got_commit_object *commit; @@ -10504,7 +10508,7 @@ rebase_commit(struct got_pathlist_head *merged_paths, error = got_worktree_rebase_commit(&new_commit_id, merged_paths, worktree, fileindex, tmp_branch, committer, commit, commit_id, - repo); + allow_conflict, repo); if (error) { if (error->code != GOT_ERR_COMMIT_NO_CHANGES) goto done; @@ -11000,6 +11004,7 @@ cmd_rebase(int argc, char *argv[]) int ch, rebase_in_progress = 0, abort_rebase = 0, continue_rebase = 0; int histedit_in_progress = 0, merge_in_progress = 0; int create_backup = 1, list_backups = 0, delete_backups = 0; + int allow_conflict = 0; struct got_object_id_queue commits; struct got_pathlist_head merged_paths; const struct got_object_id_queue *parent_ids; @@ -11017,11 +11022,14 @@ cmd_rebase(int argc, char *argv[]) err(1, "pledge"); #endif - while ((ch = getopt(argc, argv, "aclX")) != -1) { + while ((ch = getopt(argc, argv, "aCclX")) != -1) { switch (ch) { case 'a': abort_rebase = 1; break; + case 'C': + allow_conflict = 1; + break; case 'c': continue_rebase = 1; break; @@ -11043,6 +11051,8 @@ cmd_rebase(int argc, char *argv[]) if (list_backups) { if (abort_rebase) option_conflict('l', 'a'); + if (allow_conflict) + option_conflict('l', 'C'); if (continue_rebase) option_conflict('l', 'c'); if (delete_backups) @@ -11052,12 +11062,19 @@ cmd_rebase(int argc, char *argv[]) } else if (delete_backups) { if (abort_rebase) option_conflict('X', 'a'); + if (allow_conflict) + option_conflict('X', 'C'); if (continue_rebase) option_conflict('X', 'c'); if (list_backups) option_conflict('l', 'X'); if (argc != 0 && argc != 1) usage_rebase(); + } else if (allow_conflict) { + if (abort_rebase) + option_conflict('C', 'a'); + if (!continue_rebase) + errx(1, "-C option requires -c"); } else { if (abort_rebase && continue_rebase) usage_rebase(); @@ -11177,7 +11194,7 @@ cmd_rebase(int argc, char *argv[]) goto done; error = rebase_commit(NULL, worktree, fileindex, tmp_branch, - committer, resume_commit_id, repo); + committer, resume_commit_id, allow_conflict, repo); if (error) goto done; @@ -11356,7 +11373,7 @@ cmd_rebase(int argc, char *argv[]) } error = rebase_commit(&merged_paths, worktree, fileindex, - tmp_branch, committer, commit_id, repo); + tmp_branch, committer, commit_id, 0, repo); got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH); if (error) goto done; @@ -11427,7 +11444,7 @@ done: __dead static void usage_histedit(void) { - fprintf(stderr, "usage: %s histedit [-acdeflmX] [-F histedit-script] " + fprintf(stderr, "usage: %s histedit [-aCcdeflmX] [-F histedit-script] " "[branch]\n", getprogname()); exit(1); } @@ -12167,7 +12184,7 @@ static const struct got_error * histedit_commit(struct got_pathlist_head *merged_paths, struct got_worktree *worktree, struct got_fileindex *fileindex, struct got_reference *tmp_branch, struct got_histedit_list_entry *hle, - const char *committer, struct got_repository *repo) + const char *committer, int allow_conflict, struct got_repository *repo) { const struct got_error *err; struct got_commit_object *commit; @@ -12186,7 +12203,7 @@ histedit_commit(struct got_pathlist_head *merged_paths err = got_worktree_histedit_commit(&new_commit_id, merged_paths, worktree, fileindex, tmp_branch, committer, commit, hle->commit_id, - hle->logmsg, repo); + hle->logmsg, allow_conflict, repo); if (err) { if (err->code != GOT_ERR_COMMIT_NO_CHANGES) goto done; @@ -12271,7 +12288,7 @@ cmd_histedit(int argc, char *argv[]) struct got_update_progress_arg upa; int edit_in_progress = 0, abort_edit = 0, continue_edit = 0; int drop_only = 0, edit_logmsg_only = 0, fold_only = 0, edit_only = 0; - int list_backups = 0, delete_backups = 0; + int allow_conflict = 0, list_backups = 0, delete_backups = 0; const char *edit_script_path = NULL; struct got_object_id_queue commits; struct got_pathlist_head merged_paths; @@ -12292,11 +12309,14 @@ cmd_histedit(int argc, char *argv[]) err(1, "pledge"); #endif - while ((ch = getopt(argc, argv, "acdeF:flmX")) != -1) { + while ((ch = getopt(argc, argv, "aCcdeF:flmX")) != -1) { switch (ch) { case 'a': abort_edit = 1; break; + case 'C': + allow_conflict = 1; + break; case 'c': continue_edit = 1; break; @@ -12330,16 +12350,24 @@ cmd_histedit(int argc, char *argv[]) argc -= optind; argv += optind; + if (abort_edit && allow_conflict) + option_conflict('a', 'C'); if (abort_edit && continue_edit) option_conflict('a', 'c'); + if (edit_script_path && allow_conflict) + option_conflict('F', 'C'); if (edit_script_path && edit_logmsg_only) option_conflict('F', 'm'); if (abort_edit && edit_logmsg_only) option_conflict('a', 'm'); + if (edit_logmsg_only && allow_conflict) + option_conflict('m', 'C'); if (continue_edit && edit_logmsg_only) option_conflict('c', 'm'); if (abort_edit && fold_only) option_conflict('a', 'f'); + if (fold_only && allow_conflict) + option_conflict('f', 'C'); if (continue_edit && fold_only) option_conflict('c', 'f'); if (fold_only && edit_logmsg_only) @@ -12358,6 +12386,8 @@ cmd_histedit(int argc, char *argv[]) option_conflict('f', 'e'); if (drop_only && abort_edit) option_conflict('d', 'a'); + if (drop_only && allow_conflict) + option_conflict('d', 'C'); if (drop_only && continue_edit) option_conflict('d', 'c'); if (drop_only && edit_logmsg_only) @@ -12371,6 +12401,8 @@ cmd_histedit(int argc, char *argv[]) if (list_backups) { if (abort_edit) option_conflict('l', 'a'); + if (allow_conflict) + option_conflict('l', 'C'); if (continue_edit) option_conflict('l', 'c'); if (edit_script_path) @@ -12390,6 +12422,8 @@ cmd_histedit(int argc, char *argv[]) } else if (delete_backups) { if (abort_edit) option_conflict('X', 'a'); + if (allow_conflict) + option_conflict('X', 'C'); if (continue_edit) option_conflict('X', 'c'); if (drop_only) @@ -12406,7 +12440,9 @@ cmd_histedit(int argc, char *argv[]) option_conflict('X', 'l'); if (argc != 0 && argc != 1) usage_histedit(); - } else if (argc != 0) + } else if (allow_conflict && !continue_edit) + errx(1, "-C option requires -c"); + else if (argc != 0) usage_histedit(); /* @@ -12722,7 +12758,7 @@ cmd_histedit(int argc, char *argv[]) if (have_changes) { error = histedit_commit(NULL, worktree, fileindex, tmp_branch, hle, - committer, repo); + committer, allow_conflict, repo); if (error) goto done; } else { @@ -12798,7 +12834,7 @@ cmd_histedit(int argc, char *argv[]) } error = histedit_commit(&merged_paths, worktree, fileindex, - tmp_branch, hle, committer, repo); + tmp_branch, hle, committer, allow_conflict, repo); got_pathlist_free(&merged_paths, GOT_PATHLIST_FREE_PATH); if (error) goto done; @@ -13031,7 +13067,7 @@ done: __dead static void usage_merge(void) { - fprintf(stderr, "usage: %s merge [-acn] [branch]\n", getprogname()); + fprintf(stderr, "usage: %s merge [-aCcn] [branch]\n", getprogname()); exit(1); } @@ -13047,7 +13083,7 @@ cmd_merge(int argc, char *argv[]) struct got_object_id *branch_tip = NULL, *yca_id = NULL; struct got_object_id *wt_branch_tip = NULL; int ch, merge_in_progress = 0, abort_merge = 0, continue_merge = 0; - int interrupt_merge = 0; + int allow_conflict = 0, interrupt_merge = 0; struct got_update_progress_arg upa; struct got_object_id *merge_commit_id = NULL; char *branch_name = NULL; @@ -13061,11 +13097,13 @@ cmd_merge(int argc, char *argv[]) err(1, "pledge"); #endif - while ((ch = getopt(argc, argv, "acn")) != -1) { + while ((ch = getopt(argc, argv, "aCcn")) != -1) { switch (ch) { case 'a': abort_merge = 1; break; + case 'C': + allow_conflict = 1; case 'c': continue_merge = 1; break; @@ -13081,6 +13119,12 @@ cmd_merge(int argc, char *argv[]) argc -= optind; argv += optind; + if (allow_conflict) { + if (abort_merge) + option_conflict('a', 'C'); + if (!continue_merge) + errx(1, "-C option requires -c"); + } if (abort_merge && continue_merge) option_conflict('a', 'c'); if (abort_merge || continue_merge) { @@ -13269,7 +13313,8 @@ cmd_merge(int argc, char *argv[]) } else { error = got_worktree_merge_commit(&merge_commit_id, worktree, fileindex, author, NULL, 1, branch_tip, branch_name, - repo, continue_merge ? print_status : NULL, NULL); + allow_conflict, repo, continue_merge ? print_status : NULL, + NULL); if (error) goto done; error = got_worktree_merge_complete(worktree, fileindex, repo); blob - 4ea02aaea560daeba058d4b8541f5ea222eb7586 blob + f7de090bfb2bd10312bdd0c389b5eabe41a33fdd --- include/got_worktree.h +++ include/got_worktree.h @@ -257,7 +257,7 @@ typedef const struct got_error *(*got_worktree_commit_ */ const struct got_error *got_worktree_commit(struct got_object_id **, struct got_worktree *, struct got_pathlist_head *, const char *, - const char *, int, int, got_worktree_commit_msg_cb, void *, + const char *, int, int, int, got_worktree_commit_msg_cb, void *, got_worktree_status_cb, void *, struct got_repository *); /* Get the path of a commitable worktree item. */ @@ -318,7 +318,7 @@ const struct got_error *got_worktree_rebase_merge_file const struct got_error *got_worktree_rebase_commit(struct got_object_id **, struct got_pathlist_head *, struct got_worktree *, struct got_fileindex *, struct got_reference *, const char *, struct got_commit_object *, - struct got_object_id *, struct got_repository *); + struct got_object_id *, int, struct got_repository *); /* Postpone the rebase operation. Should be called after a merge conflict. */ const struct got_error *got_worktree_rebase_postpone(struct got_worktree *, @@ -392,7 +392,7 @@ const struct got_error *got_worktree_histedit_merge_fi const struct got_error *got_worktree_histedit_commit(struct got_object_id **, struct got_pathlist_head *, struct got_worktree *, struct got_fileindex *, struct got_reference *, const char *, struct got_commit_object *, - struct got_object_id *, const char *, struct got_repository *); + struct got_object_id *, const char *, int, struct got_repository *); /* * Record the specified commit as skipped during histedit. @@ -469,7 +469,7 @@ got_worktree_merge_commit(struct got_object_id **new_c struct got_worktree *worktree, struct got_fileindex *fileindex, const char *author, const char *committer, int allow_bad_symlinks, struct got_object_id *branch_tip, const char *branch_name, - struct got_repository *repo, + int allow_conflict, struct got_repository *repo, got_worktree_status_cb status_cb, void *status_arg); /* blob - c4fc2f02d953c285d633a68f2ef9f0cebc512bca blob + 055da929b846e6b27b801620463ae896ca9faf0a --- lib/worktree.c +++ lib/worktree.c @@ -1513,9 +1513,14 @@ done: return err; } -/* Upgrade STATUS_MODIFY to STATUS_CONFLICT if a conflict marker is found. */ +/* + * Upgrade STATUS_MODIFY to STATUS_CONFLICT if a + * conflict marker is found in newly added lines only. + */ static const struct got_error * -get_modified_file_content_status(unsigned char *status, FILE *f) +get_modified_file_content_status(unsigned char *status, + struct got_blob_object *blob, const char *path, struct stat *sb, + FILE *ondisk_file) { const struct got_error *err = NULL; const char *markers[3] = { @@ -1523,11 +1528,46 @@ get_modified_file_content_status(unsigned char *status GOT_DIFF_CONFLICT_MARKER_SEP, GOT_DIFF_CONFLICT_MARKER_END }; + FILE *f = NULL, *f1 = NULL; int i = 0; char *line = NULL; size_t linesize = 0; ssize_t linelen; + off_t sz1 = 0; + if (*status == GOT_STATUS_MODIFY) { + f = got_opentemp(); + if (f == NULL) + return got_error_from_errno("got_opentemp"); + + f1 = got_opentemp(); + if (f1 == NULL) { + err = got_error_from_errno("got_opentemp"); + goto done; + } + + if (blob) { + err = got_object_blob_dump_to_file(&sz1, NULL, NULL, + f1, blob); + if (err) + goto done; + } + + err = got_diff_blob_file(blob, f1, sz1, NULL, ondisk_file, 1, + sb, path, GOT_DIFF_ALGORITHM_MYERS, 0, 0, 0, NULL, f); + if (err) + goto done; + + if (fflush(f) == EOF) { + err = got_error_from_errno("fflush"); + goto done; + } + if (fseeko(f, 0L, SEEK_SET) == -1) { + err = got_error_from_errno("fseek"); + goto done; + } + } + while (*status == GOT_STATUS_MODIFY) { linelen = getline(&line, &linesize, f); if (linelen == -1) { @@ -1537,7 +1577,8 @@ get_modified_file_content_status(unsigned char *status break; } - if (strncmp(line, markers[i], strlen(markers[i])) == 0) { + if (*line == '+' && + strncmp(line + 1, markers[i], strlen(markers[i])) == 0) { if (strcmp(markers[i], GOT_DIFF_CONFLICT_MARKER_END) == 0) *status = GOT_STATUS_CONFLICT; @@ -1545,7 +1586,13 @@ get_modified_file_content_status(unsigned char *status i++; } } + +done: free(line); + if (f != NULL && fclose(f) == EOF && err == NULL) + err = got_error_from_errno("fclose"); + if (f1 != NULL && fclose(f1) == EOF && err == NULL) + err = got_error_from_errno("fclose"); return err; } @@ -1786,7 +1833,8 @@ get_file_status(unsigned char *status, struct stat *sb if (*status == GOT_STATUS_MODIFY) { rewind(f); - err = get_modified_file_content_status(status, f); + err = get_modified_file_content_status(status, blob, ie->path, + sb, f); } else if (xbit_differs(ie, sb->st_mode)) *status = GOT_STATUS_MODE_CHANGE; done: @@ -4947,6 +4995,7 @@ struct collect_commitables_arg { int have_staged_files; int allow_bad_symlinks; int diff_header_shown; + int commit_conflicts; FILE *diff_outfile; FILE *f1; FILE *f2; @@ -5023,7 +5072,8 @@ append_ct_diff(struct got_commitable *ct, int *diff_he } else { if (ct->status != GOT_STATUS_MODIFY && ct->status != GOT_STATUS_ADD && - ct->status != GOT_STATUS_DELETE) + ct->status != GOT_STATUS_DELETE && + ct->status != GOT_STATUS_CONFLICT) return NULL; } @@ -5180,13 +5230,16 @@ collect_commitables(void *arg, unsigned char status, staged_status != GOT_STATUS_DELETE) return NULL; } else { - if (status == GOT_STATUS_CONFLICT) + if (status == GOT_STATUS_CONFLICT && !a->commit_conflicts) { + printf("C %s\n", relpath); return got_error(GOT_ERR_COMMIT_CONFLICT); + } if (status != GOT_STATUS_MODIFY && status != GOT_STATUS_MODE_CHANGE && status != GOT_STATUS_ADD && - status != GOT_STATUS_DELETE) + status != GOT_STATUS_DELETE && + status != GOT_STATUS_CONFLICT) return NULL; } @@ -5536,7 +5589,8 @@ match_deleted_or_modified_ct(struct got_commitable **c if (ct->staged_status == GOT_STATUS_NO_CHANGE) { if (ct->status != GOT_STATUS_MODIFY && ct->status != GOT_STATUS_MODE_CHANGE && - ct->status != GOT_STATUS_DELETE) + ct->status != GOT_STATUS_DELETE && + ct->status != GOT_STATUS_CONFLICT) continue; } else { if (ct->staged_status != GOT_STATUS_MODIFY && @@ -5744,6 +5798,7 @@ write_tree(struct got_object_id **new_tree_id, int *ne /* NB: Deleted entries get dropped here. */ if (ct->status == GOT_STATUS_MODIFY || ct->status == GOT_STATUS_MODE_CHANGE || + ct->status == GOT_STATUS_CONFLICT || ct->staged_status == GOT_STATUS_MODIFY) { err = alloc_modified_blob_tree_entry( &new_te, te, ct); @@ -5953,7 +6008,8 @@ commit_worktree(struct got_object_id **new_commit_id, if (ct->status != GOT_STATUS_ADD && ct->status != GOT_STATUS_MODIFY && - ct->status != GOT_STATUS_MODE_CHANGE) + ct->status != GOT_STATUS_MODE_CHANGE && + ct->status != GOT_STATUS_CONFLICT) continue; if (asprintf(&ondisk_path, "%s/%s", @@ -6104,7 +6160,8 @@ const struct got_error * got_worktree_commit(struct got_object_id **new_commit_id, struct got_worktree *worktree, struct got_pathlist_head *paths, const char *author, const char *committer, int allow_bad_symlinks, - int show_diff, got_worktree_commit_msg_cb commit_msg_cb, void *commit_arg, + int show_diff, int commit_conflicts, + got_worktree_commit_msg_cb commit_msg_cb, void *commit_arg, got_worktree_status_cb status_cb, void *status_arg, struct got_repository *repo) { @@ -6157,6 +6214,7 @@ got_worktree_commit(struct got_object_id **new_commit_ cc_arg.have_staged_files = have_staged_files; cc_arg.allow_bad_symlinks = allow_bad_symlinks; cc_arg.diff_header_shown = 0; + cc_arg.commit_conflicts = commit_conflicts; if (show_diff) { err = got_opentemp_named(&diff_path, &cc_arg.diff_outfile, GOT_TMPDIR_STR "/got", ".diff"); @@ -6713,7 +6771,7 @@ rebase_commit(struct got_object_id **new_commit_id, struct got_worktree *worktree, struct got_fileindex *fileindex, struct got_reference *tmp_branch, const char *committer, struct got_commit_object *orig_commit, const char *new_logmsg, - struct got_repository *repo) + int allow_conflict, struct got_repository *repo) { const struct got_error *err, *sync_err; struct got_pathlist_head commitable_paths; @@ -6737,6 +6795,7 @@ rebase_commit(struct got_object_id **new_commit_id, cc_arg.worktree = worktree; cc_arg.repo = repo; cc_arg.have_staged_files = 0; + cc_arg.commit_conflicts = allow_conflict; /* * If possible get the status of individual files directly to * avoid crawling the entire work tree once per rebased commit. @@ -6832,7 +6891,8 @@ got_worktree_rebase_commit(struct got_object_id **new_ struct got_pathlist_head *merged_paths, struct got_worktree *worktree, struct got_fileindex *fileindex, struct got_reference *tmp_branch, const char *committer, struct got_commit_object *orig_commit, - struct got_object_id *orig_commit_id, struct got_repository *repo) + struct got_object_id *orig_commit_id, int allow_conflict, + struct got_repository *repo) { const struct got_error *err; char *commit_ref_name; @@ -6856,7 +6916,7 @@ got_worktree_rebase_commit(struct got_object_id **new_ err = rebase_commit(new_commit_id, merged_paths, commit_ref, worktree, fileindex, tmp_branch, committer, orig_commit, - NULL, repo); + NULL, allow_conflict, repo); done: if (commit_ref) got_ref_close(commit_ref); @@ -6871,7 +6931,7 @@ got_worktree_histedit_commit(struct got_object_id **ne struct got_fileindex *fileindex, struct got_reference *tmp_branch, const char *committer, struct got_commit_object *orig_commit, struct got_object_id *orig_commit_id, const char *new_logmsg, - struct got_repository *repo) + int allow_conflict, struct got_repository *repo) { const struct got_error *err; char *commit_ref_name; @@ -6887,7 +6947,7 @@ got_worktree_histedit_commit(struct got_object_id **ne err = rebase_commit(new_commit_id, merged_paths, commit_ref, worktree, fileindex, tmp_branch, committer, orig_commit, - new_logmsg, repo); + new_logmsg, allow_conflict, repo); done: if (commit_ref) got_ref_close(commit_ref); @@ -7852,7 +7912,7 @@ got_worktree_merge_commit(struct got_object_id **new_c struct got_worktree *worktree, struct got_fileindex *fileindex, const char *author, const char *committer, int allow_bad_symlinks, struct got_object_id *branch_tip, const char *branch_name, - struct got_repository *repo, + int allow_conflict, struct got_repository *repo, got_worktree_status_cb status_cb, void *status_arg) { @@ -7898,6 +7958,7 @@ got_worktree_merge_commit(struct got_object_id **new_c cc_arg.repo = repo; cc_arg.have_staged_files = have_staged_files; cc_arg.allow_bad_symlinks = allow_bad_symlinks; + cc_arg.commit_conflicts = allow_conflict; err = worktree_status(worktree, "", fileindex, repo, collect_commitables, &cc_arg, NULL, NULL, 1, 0); if (err) blob - 0543d4bebab658396a069f968553a9b43594ca74 blob + 488cdaf04a738a2e6a11df99d0d5df6b7a604c83 --- regress/cmdline/commit.sh +++ regress/cmdline/commit.sh @@ -294,6 +294,7 @@ test_commit_rejects_conflicted_file() { echo "modified alpha" > $testroot/wt/alpha (cd $testroot/wt && got commit -m "modified alpha" >/dev/null) + local commit_id1=`git_show_head $testroot/repo` (cd $testroot/wt && got update -c $initial_rev > /dev/null) @@ -317,8 +318,14 @@ test_commit_rejects_conflicted_file() { (cd $testroot/wt && got commit -m 'commit it' > $testroot/stdout \ 2> $testroot/stderr) + ret=$? + if [ $ret -eq 0 ]; then + echo "got commit succeeded unexpectedly" + test_done "$testroot" "$ret" + return 1 + fi - echo -n > $testroot/stdout.expected + echo "C alpha" > $testroot/stdout.expected echo "got: cannot commit file in conflicted status" \ > $testroot/stderr.expected @@ -333,7 +340,72 @@ test_commit_rejects_conflicted_file() { ret=$? if [ $ret -ne 0 ]; then diff -u $testroot/stderr.expected $testroot/stderr + test_done "$testroot" "$ret" + return 1 + fi + + (cd $testroot/wt && got commit -C -m 'commit it' > $testroot/stdout \ + 2> $testroot/stderr) + ret=$? + if [ $ret -ne 0 ]; then + echo "got commit failed unexpectedly" + test_done "$testroot" "$ret" + return 1 + fi + + # make sure the conflicted commit produces a diff + local conflict_commit=`git_show_head $testroot/repo` + local blob_minus=`got tree -r $testroot/repo -c $commit_id1 -i | \ + grep 'alpha$' | cut -d' ' -f1` + local blob_plus=`got tree -r $testroot/repo -c $conflict_commit -i | \ + grep 'alpha$' | cut -d' ' -f1` + + echo -n > $testroot/stderr.expected + cmp -s $testroot/stderr.expected $testroot/stderr + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stderr.expected $testroot/stderr + test_done "$testroot" "$ret" + return 1 + fi + + (cd $testroot/wt && got diff -c master > $testroot/stdout) + + echo -n > $testroot/stdout.expected + cat > $testroot/stdout.expected <>>>>>> +EOF + + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 fi + + (cd $testroot/wt && got status > $testroot/stdout) + + echo -n > $testroot/stdout.expected + cmp -s $testroot/stdout.expected $testroot/stdout + ret=$? + if [ $ret -ne 0 ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi test_done "$testroot" "$ret" } blob - afb98ebb0b2de07c90dfa887d9d20fe0cb5f0db4 blob + 1ab6bfe75bae74735e4040c6d98ea1dea7cd9dc3 --- regress/cmdline/update.sh +++ regress/cmdline/update.sh @@ -2970,7 +2970,7 @@ test_update_binary_file() { fi (cd $testroot/wt && got status > $testroot/stdout) - echo 'C foo' > $testroot/stdout.expected + echo 'M foo' > $testroot/stdout.expected echo -n '? ' >> $testroot/stdout.expected (cd $testroot/wt && ls foo-1-* >> $testroot/stdout.expected) echo -n '? ' >> $testroot/stdout.expected