commit - 9c986b77d3f71b37ff5b5c6b4ec58b495ad8f312
commit + c935fd512b9937ffefdd248a3a840d0530011a1e
blob - 53e320eef8622a3384a31606d0cd2fe64d9ea60a
blob + 07cb22fd98f7b10c110a8a5d0c0627770c5b7e76
--- include/got_worktree.h
+++ include/got_worktree.h
*/
const struct got_error *got_worktree_set_base_commit_id(struct got_worktree *,
struct got_repository *, struct got_object_id *);
+
+/*
+ * Get the state of the work tree. If the work tree's global base commit is
+ * the tip of the work tree's current branch, and each file in the index is
+ * based on this same commit, the char out parameter will be
+ * GOT_WORKTREE_UPTODATE, else it will be GOT_WORKTREE_OUTOFDATE.
+ */
+const struct got_error *got_worktree_get_state(char *,
+ struct got_repository *, struct got_worktree *);
+#define GOT_WORKTREE_UPTODATE '*'
+#define GOT_WORKTREE_OUTOFDATE '~'
+
/*
* Obtain a parsed representation of this worktree's got.conf file.
* Return NULL if this configuration file could not be read.
blob - 5400e1336138a373e1307ff721ae4d68d5633075
blob + 6b14bc178bf5fbc6065088656b0ecae4a8fd2aad
--- lib/worktree.c
+++ lib/worktree.c
return NULL;
}
+const struct got_error *
+got_worktree_get_state(char *state, struct got_repository *repo,
+ struct got_worktree *worktree)
+{
+ const struct got_error *err;
+ struct got_object_id *base_id, *head_id = NULL;
+ struct got_reference *head_ref;
+ struct got_fileindex *fileindex = NULL;
+ char *fileindex_path = NULL;
+
+ if (worktree == NULL)
+ return got_error(GOT_ERR_NOT_WORKTREE);
+
+ err = got_ref_open(&head_ref, repo,
+ got_worktree_get_head_ref_name(worktree), 0);
+ if (err)
+ return err;
+
+ err = got_ref_resolve(&head_id, repo, head_ref);
+ if (err)
+ goto done;
+
+ *state = GOT_WORKTREE_OUTOFDATE;
+ base_id = got_worktree_get_base_commit_id(worktree);
+
+ if (got_object_id_cmp(base_id, head_id) == 0) {
+ err = open_fileindex(&fileindex, &fileindex_path, worktree);
+ if (err)
+ goto done;
+
+ err = got_fileindex_for_each_entry_safe(fileindex,
+ check_mixed_commits, worktree);
+ if (err == NULL)
+ *state = GOT_WORKTREE_UPTODATE;
+ else if (err->code == GOT_ERR_MIXED_COMMITS)
+ err = NULL;
+ }
+
+done:
+ free(head_id);
+ free(fileindex_path);
+ got_ref_close(head_ref);
+ if (fileindex != NULL)
+ got_fileindex_free(fileindex);
+ return err;
+}
+
struct check_merge_conflicts_arg {
struct got_worktree *worktree;
struct got_fileindex *fileindex;
blob - 2a28c65c9bf9e227cba943ce259438976ac67c02
blob + d10c8e4e269c29522a88fa8b7f94b5658cacd469
--- regress/tog/log.sh
+++ regress/tog/log.sh
commit $(pop_id 5 $ids) [1/5]
$ymd $(pop_id 5 $short_ids) flan_hacker commit 4
$ymd $(pop_id 4 $short_ids) flan_hacker commit 3
- $ymd $(pop_id 3 $short_ids) flan_hacker commit 2
+ $ymd $(pop_id 3 $short_ids) flan_hacker ~commit 2
$ymd $(pop_id 2 $short_ids) flan_hacker commit 1
$ymd $(pop_id 1 $short_ids) flan_hacker adding the test tree
test_done "$testroot" "$ret"
}
+
+test_log_show_base_commit()
+{
+ # make view wide enough to show full headline
+ test_init log_show_base_commit 80 3
+ local repo="$testroot/repo"
+ local id=$(git_show_head "$repo")
+
+ echo "alpha" >> "$repo/alpha"
+ git_commit "$repo" -m "base commit"
+ got checkout "$repo" "$testroot/wt" > /dev/null
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ echo "got checkout failed unexpectedly"
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # move into the work tree (test is run in a subshell)
+ cd "$testroot/wt"
+
+ local head_id=$(git_show_head "$repo")
+ local author_time=$(git_show_author_time "$repo")
+ local ymd=$(date -u -r "$author_time" +"%G-%m-%d")
+
+ # check up-to-date base commit marker prefixes base commit log message
+ cat <<-EOF >$TOG_TEST_SCRIPT
+ SCREENDUMP
+ EOF
+
+ cat <<-EOF >$testroot/view.expected
+ commit $head_id [1/2] master
+ $ymd flan_hacker *[master] base commit
+ $ymd flan_hacker adding the test tree
+ EOF
+
+ tog log
+ cmp -s "$testroot/view.expected" "$testroot/view"
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u "$testroot/view.expected" "$testroot/view"
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # check marker is not drawn when not in a work tree
+ cat <<-EOF >$testroot/view.expected
+ commit $head_id [1/2] master
+ $ymd flan_hacker [master] base commit
+ $ymd flan_hacker adding the test tree
+ EOF
+
+ tog log -r "$repo"
+ cmp -s "$testroot/view.expected" "$testroot/view"
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u "$testroot/view.expected" "$testroot/view"
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # check out-of-date marker is shown with a mixed-commit tree
+ echo "mixed" > alpha
+ got commit -m "new base mixed-commit" > /dev/null
+ head_id=$(git_show_head "$repo")
+
+ cat <<-EOF >$TOG_TEST_SCRIPT
+ SCREENDUMP
+ EOF
+
+ cat <<-EOF >$testroot/view.expected
+ commit $head_id [1/3] master
+ $ymd flan_hacker ~[master] new base mixed-commit
+ $ymd flan_hacker base commit
+ EOF
+
+ tog log
+ cmp -s "$testroot/view.expected" "$testroot/view"
+ ret=$?
+ if [ $ret -ne 0 ]; then
+ diff -u "$testroot/view.expected" "$testroot/view"
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ test_done "$testroot" "$ret"
+}
+
test_parseargs "$@"
run_test test_log_hsplit_diff
run_test test_log_vsplit_diff
run_test test_log_hsplit_tree
run_test test_log_logmsg_widechar
run_test test_log_commit_keywords
+run_test test_log_show_base_commit
blob - 816fab05d74cd0ab51f96eb3cad17d26d95b56e3
blob + 6b4cb836095040c424af47d6a41111f9f98496b5
--- tog/tog.1
+++ tog/tog.1
is interpreted relative to the current working directory,
and the work tree's path prefix is implicitly prepended.
Otherwise, the path is interpreted relative to the repository root.
+.Pp
+If invoked in a work tree, the log entry of the work tree's base commit will
+be prefixed with one of the following annotations:
+.Bl -column YXZ description
+.It * Ta work tree's base commit and the base commit of all tracked files
+matches the branch tip
+.It \(a~ Ta work tree comprises mixed commits or its base commit is out-of-date
+.El
.Pp
This command is also executed if no explicit command is specified.
.Pp
blob - 55201d879df128d62d2a76ec797059af85649fa8
blob + 00b76714868599ae292351b665e7a84bbeb85b81
--- tog/tog.c
+++ tog/tog.c
static struct got_reflist_head tog_refs = TAILQ_HEAD_INITIALIZER(tog_refs);
static struct got_reflist_object_id_map *tog_refs_idmap;
+static struct {
+ struct got_object_id *id;
+ int idx;
+ char marker;
+} tog_base_commit;
static enum got_diff_algorithm tog_diff_algo = GOT_DIFF_ALGORITHM_MYERS;
static const struct got_error *
}
static const struct got_error *
-draw_commit(struct tog_view *view, struct got_commit_object *commit,
- struct got_object_id *id, const size_t date_display_cols,
- int author_display_cols)
+draw_commit(struct tog_view *view, struct commit_queue_entry *entry,
+ const size_t date_display_cols, int author_display_cols)
{
struct tog_log_view_state *s = &view->state.log;
const struct got_error *err = NULL;
+ struct got_commit_object *commit = entry->commit;
+ struct got_object_id *id = entry->id;
char datebuf[12]; /* YYYY-MM-DD + SPACE + NUL */
char *refs_str = NULL;
char *logmsg0 = NULL, *logmsg = NULL;
int author_width, refstr_width, logmsg_width;
char *newline, *line = NULL;
int col, limit, scrollx, logmsg_x;
- const int avail = view->ncols;
+ const int avail = view->ncols, marker_column = author_display_cols + 1;
struct tm tm;
time_t committer_time;
struct tog_color *tc;
waddwstr(view->window, wauthor);
col += author_width;
while (col < avail && author_width < author_display_cols + 2) {
- waddch(view->window, ' ');
+ if (tog_base_commit.id != NULL &&
+ author_width == marker_column &&
+ entry->idx == tog_base_commit.idx)
+ waddch(view->window, tog_base_commit.marker);
+ else
+ waddch(view->window, ' ');
col++;
author_width++;
}
TAILQ_INSERT_TAIL(&a->real_commits->head, entry, entry);
a->real_commits->ncommits++;
+ if (tog_base_commit.id != NULL && tog_base_commit.idx == -1 &&
+ got_object_id_cmp(&id, tog_base_commit.id) == 0)
+ tog_base_commit.idx = entry->idx;
+
if (*a->limiting) {
err = match_commit(&limit_match, &id, commit,
a->limit_regex);
break;
if (ncommits == s->selected)
wstandout(view->window);
- err = draw_commit(view, entry->commit, entry->id,
- date_display_cols, author_cols);
+ err = draw_commit(view, entry, date_display_cols, author_cols);
if (ncommits == s->selected)
wstandend(view->window);
if (err)
}
static const struct got_error *
+set_tog_base_commit(struct got_repository *repo, struct got_worktree *worktree)
+{
+ tog_base_commit.id = got_object_id_dup(
+ got_worktree_get_base_commit_id(worktree));
+ if (tog_base_commit.id == NULL)
+ return got_error_from_errno( "got_object_id_dup");
+
+ tog_base_commit.idx = -1;
+
+ return got_worktree_get_state(&tog_base_commit.marker, repo,
+ worktree);
+}
+
+static const struct got_error *
get_in_repo_path_from_argv0(char **in_repo_path, int argc, char *argv[],
struct got_repository *repo, struct got_worktree *worktree)
{
in_repo_path, log_branches);
if (error)
goto done;
+
if (worktree) {
+ error = set_tog_base_commit(repo, worktree);
+ if (error != NULL)
+ goto done;
+
/* Release work tree lock. */
got_worktree_close(worktree);
worktree = NULL;
}
+
error = view_loop(view);
+
done:
+ free(tog_base_commit.id);
free(keyword_idstr);
free(in_repo_path);
free(repo_path);
ignore_whitespace, force_text_diff, NULL, repo);
if (error)
goto done;
+
+ if (worktree) {
+ error = set_tog_base_commit(repo, worktree);
+ if (error != NULL)
+ goto done;
+ }
+
error = view_loop(view);
+
done:
+ free(tog_base_commit.id);
free(keyword_idstr1);
free(keyword_idstr2);
free(label1);
view_close(view);
goto done;
}
+
if (worktree) {
+ error = set_tog_base_commit(repo, worktree);
+ if (error != NULL)
+ goto done;
+
/* Release work tree lock. */
got_worktree_close(worktree);
worktree = NULL;
}
+
error = view_loop(view);
+
done:
+ free(tog_base_commit.id);
free(repo_path);
free(in_repo_path);
free(link_target);
}
if (worktree) {
+ error = set_tog_base_commit(repo, worktree);
+ if (error != NULL)
+ goto done;
+
/* Release work tree lock. */
got_worktree_close(worktree);
worktree = NULL;
}
+
error = view_loop(view);
+
done:
+ free(tog_base_commit.id);
free(keyword_idstr);
free(repo_path);
free(cwd);
free(label);
if (ref)
got_ref_close(ref);
+ if (worktree != NULL)
+ got_worktree_close(worktree);
if (repo) {
const struct got_error *close_err = got_repo_close(repo);
if (error == NULL)
goto done;
if (worktree) {
+ error = set_tog_base_commit(repo, worktree);
+ if (error != NULL)
+ goto done;
+
/* Release work tree lock. */
got_worktree_close(worktree);
worktree = NULL;
}
+
error = view_loop(view);
+
done:
+ free(tog_base_commit.id);
free(repo_path);
free(cwd);
+ if (worktree != NULL)
+ got_worktree_close(worktree);
if (repo) {
const struct got_error *close_err = got_repo_close(repo);
if (close_err)