commit - ccecc9fd21b05e615c5ea47b2308c048b0fd7816
commit + d1fe46f97f891c3ad339bdac5b2eabcd93621b32
blob - 3b3b0751571a29882714bb2e35cf3e14420f4113
blob + 6583cc3872bc92bf71d69523894f2b671cdfb368
--- got/got.1
+++ got/got.1
.It Cm st
Short alias for
.Cm status .
-.It Cm log Oo Fl b Oc Oo Fl c Ar commit Oc Oo Fl C Ar number Oc Oo Fl l Ar N Oc Oo Fl p Oc Oo Fl s Ar search-pattern Oc Oo Fl r Ar repository-path Oc Op Ar path
+.It Cm log Oo Fl b Oc Oo Fl c Ar commit Oc Oo Fl C Ar number Oc Oo Fl l Ar N Oc Oo Fl p Oc Oo Fl s Ar search-pattern Oc Oo Fl r Ar repository-path Oc Oo Fl x Ar commit Oc Op Ar path
Display history of a repository.
If a
.Ar path
If this directory is a
.Nm
work tree, use the repository path associated with this work tree.
+.It Fl x Ar commit
+Stop displaying commits as soon as the specified
+.Ar commit
+has been displayed.
+This option has no effect if the specified
+.Ar commit
+is never traversed.
.El
.It Cm diff Oo Fl C Ar number Oc Oo Fl r Ar repository-path Oc Oo Fl s Oc Oo Fl w Oc Op Ar object1 Ar object2 | Ar path
When invoked within a work tree with less than two arguments, display
blob - cd3415665c119c7b002ed7110eaacbf0a182f345
blob + d37d12527fa04f6fa2b72c9e239519f6a7884246
--- got/got.c
+++ got/got.c
}
static const struct got_error *
-print_commits(struct got_object_id *root_id, struct got_repository *repo,
- const char *path, int show_patch, const char *search_pattern,
- int diff_context, int limit, int log_branches,
+print_commits(struct got_object_id *root_id, struct got_object_id *end_id,
+ struct got_repository *repo, const char *path, int show_patch,
+ const char *search_pattern, int diff_context, int limit, int log_branches,
struct got_reflist_head *refs)
{
const struct got_error *err;
err = print_commit(commit, id, repo, path, show_patch,
diff_context, refs);
got_object_commit_close(commit);
- if (err || (limit && --limit == 0))
+ if (err || (limit && --limit == 0) ||
+ (end_id != NULL && got_object_id_cmp(id, end_id) == 0))
break;
}
done:
__dead static void
usage_log(void)
{
- fprintf(stderr, "usage: %s log [-b] [-c commit] [-C number] [ -l N ] [-p] "
- "[-s search-pattern] [-r repository-path] [path]\n", getprogname());
+ fprintf(stderr, "usage: %s log [-b] [-c commit] [-C number] [ -l N ] "
+ "[-p] [-x commit] [-s search-pattern] [-r repository-path] "
+ "[path]\n", getprogname());
exit(1);
}
}
static const struct got_error *
+resolve_commit_arg(struct got_object_id **id, const char *commit_arg,
+ struct got_repository *repo)
+{
+ const struct got_error *err = NULL;
+ struct got_reference *ref;
+
+ *id = NULL;
+
+ err = got_ref_open(&ref, repo, commit_arg, 0);
+ if (err == NULL) {
+ int obj_type;
+ err = got_ref_resolve(id, repo, ref);
+ got_ref_close(ref);
+ if (err)
+ return err;
+ err = got_object_get_type(&obj_type, repo, *id);
+ if (err)
+ return err;
+ if (obj_type == GOT_OBJ_TYPE_TAG) {
+ struct got_tag_object *tag;
+ err = got_object_open_as_tag(&tag, repo, *id);
+ if (err)
+ return err;
+ if (got_object_tag_get_object_type(tag) !=
+ GOT_OBJ_TYPE_COMMIT) {
+ got_object_tag_close(tag);
+ return got_error(GOT_ERR_OBJ_TYPE);
+ }
+ free(*id);
+ *id = got_object_id_dup(
+ got_object_tag_get_object_id(tag));
+ if (*id == NULL)
+ err = got_error_from_errno(
+ "got_object_id_dup");
+ got_object_tag_close(tag);
+ if (err)
+ return err;
+ } else if (obj_type != GOT_OBJ_TYPE_COMMIT)
+ return got_error(GOT_ERR_OBJ_TYPE);
+ } else {
+ err = got_repo_match_object_id_prefix(id, commit_arg,
+ GOT_OBJ_TYPE_COMMIT, repo);
+ }
+
+ return err;
+}
+
+static const struct got_error *
cmd_log(int argc, char *argv[])
{
const struct got_error *error;
struct got_repository *repo = NULL;
struct got_worktree *worktree = NULL;
- struct got_commit_object *commit = NULL;
- struct got_object_id *id = NULL;
+ struct got_object_id *start_id = NULL, *end_id = NULL;
char *repo_path = NULL, *path = NULL, *cwd = NULL, *in_repo_path = NULL;
- const char *start_commit = NULL, *search_pattern = NULL;
+ const char *start_commit = NULL, *end_commit = NULL;
+ const char *search_pattern = NULL;
int diff_context = -1, ch;
int show_patch = 0, limit = 0, log_branches = 0;
const char *errstr;
limit = get_default_log_limit();
- while ((ch = getopt(argc, argv, "bpc:C:l:r:s:")) != -1) {
+ while ((ch = getopt(argc, argv, "bpc:C:l:r:s:x:")) != -1) {
switch (ch) {
case 'p':
show_patch = 1;
case 's':
search_pattern = optarg;
break;
+ case 'x':
+ end_commit = optarg;
+ break;
default:
usage_log();
/* NOTREACHED */
if (start_commit == NULL) {
struct got_reference *head_ref;
+ struct got_commit_object *commit = NULL;
error = got_ref_open(&head_ref, repo,
worktree ? got_worktree_get_head_ref_name(worktree)
: GOT_REF_HEAD, 0);
if (error != NULL)
- return error;
- error = got_ref_resolve(&id, repo, head_ref);
+ goto done;
+ error = got_ref_resolve(&start_id, repo, head_ref);
got_ref_close(head_ref);
if (error != NULL)
- return error;
- error = got_object_open_as_commit(&commit, repo, id);
+ goto done;
+ error = got_object_open_as_commit(&commit, repo,
+ start_id);
+ if (error != NULL)
+ goto done;
+ got_object_commit_close(commit);
} else {
- struct got_reference *ref;
- error = got_ref_open(&ref, repo, start_commit, 0);
- if (error == NULL) {
- int obj_type;
- error = got_ref_resolve(&id, repo, ref);
- got_ref_close(ref);
- if (error != NULL)
- goto done;
- error = got_object_get_type(&obj_type, repo, id);
- if (error != NULL)
- goto done;
- if (obj_type == GOT_OBJ_TYPE_TAG) {
- struct got_tag_object *tag;
- error = got_object_open_as_tag(&tag, repo, id);
- if (error != NULL)
- goto done;
- if (got_object_tag_get_object_type(tag) !=
- GOT_OBJ_TYPE_COMMIT) {
- got_object_tag_close(tag);
- error = got_error(GOT_ERR_OBJ_TYPE);
- goto done;
- }
- free(id);
- id = got_object_id_dup(
- got_object_tag_get_object_id(tag));
- if (id == NULL)
- error = got_error_from_errno(
- "got_object_id_dup");
- got_object_tag_close(tag);
- if (error)
- goto done;
- } else if (obj_type != GOT_OBJ_TYPE_COMMIT) {
- error = got_error(GOT_ERR_OBJ_TYPE);
- goto done;
- }
- error = got_object_open_as_commit(&commit, repo, id);
- if (error != NULL)
- goto done;
- }
- if (commit == NULL) {
- error = got_repo_match_object_id_prefix(&id,
- start_commit, GOT_OBJ_TYPE_COMMIT, repo);
- if (error != NULL)
- return error;
- }
+ error = resolve_commit_arg(&start_id, start_commit, repo);
+ if (error != NULL)
+ goto done;
}
- if (error != NULL)
- goto done;
+ if (end_commit != NULL) {
+ error = resolve_commit_arg(&end_id, end_commit, repo);
+ if (error != NULL)
+ goto done;
+ }
if (worktree) {
const char *prefix = got_worktree_get_path_prefix(worktree);
if (error)
goto done;
- error = print_commits(id, repo, path, show_patch, search_pattern,
- diff_context, limit, log_branches, &refs);
+ error = print_commits(start_id, end_id, repo, path, show_patch,
+ search_pattern, diff_context, limit, log_branches, &refs);
done:
free(path);
free(repo_path);
free(cwd);
- free(id);
if (worktree)
got_worktree_close(worktree);
if (repo) {
blob - a903a0ec15c99cef90eb0ed9aacd554443df3aef
blob + 3f4c9bd9cc92924eee23f9724c484d587cab4334
--- regress/cmdline/log.sh
+++ regress/cmdline/log.sh
test_done "$testroot" "$ret"
}
+function test_log_end_at_commit {
+ local testroot=`test_init log_end_at_commit`
+ local commit_id0=`git_show_head $testroot/repo`
+
+ got checkout $testroot/repo $testroot/wt > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo "modified alpha" > $testroot/wt/alpha
+ (cd $testroot/wt && got commit -m 'test log_limit' > /dev/null)
+ local commit_id1=`git_show_head $testroot/repo`
+
+ (cd $testroot/wt && got rm beta >/dev/null)
+ (cd $testroot/wt && got commit -m 'test log_limit' > /dev/null)
+ local commit_id2=`git_show_head $testroot/repo`
+
+ echo "new file" > $testroot/wt/new
+ (cd $testroot/wt && got add new >/dev/null)
+ (cd $testroot/wt && got commit -m 'test log_limit' > /dev/null)
+ local commit_id3=`git_show_head $testroot/repo`
+
+ # Print commit 3 only
+ echo "commit $commit_id3 (master)" > $testroot/stdout.expected
+ (cd $testroot/wt && got log -x $commit_id3 | grep ^commit \
+ > $testroot/stdout)
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # Print commit 3 up to commit 1 inclusive
+ echo "commit $commit_id3 (master)" > $testroot/stdout.expected
+ echo "commit $commit_id2" >> $testroot/stdout.expected
+ echo "commit $commit_id1" >> $testroot/stdout.expected
+ (cd $testroot/wt && got log -c $commit_id3 -x $commit_id1 | \
+ grep ^commit > $testroot/stdout)
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # Create commits on an unrelated branch
+ (cd $testroot/wt && got br foo > /dev/null)
+ echo bar >> $testroot/wt/alpha
+ (cd $testroot/wt && got commit -m "change on branch foo" >/dev/null)
+ local commit_id4=`git_show_branch_head $testroot/repo foo`
+
+ # Print commit 4 only (in work tree)
+ echo "commit $commit_id4 (foo)" > $testroot/stdout.expected
+ (cd $testroot/wt && got log -x foo | grep ^commit \
+ > $testroot/stdout)
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # Print commit 4 only (in repository)
+ echo "commit $commit_id4 (foo)" > $testroot/stdout.expected
+ (cd $testroot/repo && got log -c foo -x foo | grep ^commit \
+ > $testroot/stdout)
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # Repository's HEAD is on master branch so -x foo without an explicit
+ # '-c foo' start commit has no effect there
+ echo "commit $commit_id3 (master)" > $testroot/stdout.expected
+ echo "commit $commit_id2" >> $testroot/stdout.expected
+ echo "commit $commit_id1" >> $testroot/stdout.expected
+ echo "commit $commit_id0" >> $testroot/stdout.expected
+ (cd $testroot/repo && got log -x foo | grep ^commit \
+ > $testroot/stdout)
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # got will refuse -x with a non-existent commit
+ (cd $testroot/wt && got log -x nonexistent \
+ > $testroot/stdout 2> $testroot/stderr)
+ ret="$?"
+ if [ "$ret" == "0" ]; then
+ echo "log command succeeded unexpectedly" >&2
+ test_done "$testroot" "1"
+ return 1
+ fi
+ echo -n > $testroot/stdout.expected
+ echo "got: nonexistent: bad object id string" \
+ > $testroot/stderr.expected
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # try the same with the hash of an empty string which is very
+ # unlikely to match any object
+ (cd $testroot/wt && \
+ got log -x da39a3ee5e6b4b0d3255bfef95601890afd80709 \
+ > $testroot/stdout 2> $testroot/stderr)
+ ret="$?"
+ if [ "$ret" == "0" ]; then
+ echo "log command succeeded unexpectedly" >&2
+ test_done "$testroot" "1"
+ return 1
+ fi
+ echo -n > $testroot/stdout.expected
+ echo "got: object not found" > $testroot/stderr.expected
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ test_done "$testroot" "0"
+}
+
run_test test_log_in_repo
run_test test_log_in_bare_repo
run_test test_log_in_worktree
run_test test_log_tag
run_test test_log_limit
run_test test_log_nonexistent_path
+run_test test_log_end_at_commit