commit b72f483a5d3d0699d6ff221cd44f8853eded0a32 from: Stefan Sperling date: Tue Feb 05 23:04:59 2019 UTC make 'got diff' show local changes in work tree commit - 14e5d4dcac6e70e8ce1dc7434f4e4f5aa6dbf963 commit + b72f483a5d3d0699d6ff221cd44f8853eded0a32 blob - f755ddbe785f844e0f4a12a7a9a92106f64fdd5f blob + 6c2d08f4242ad98d6f9d4ce5c88fb3a806671493 --- got/got.1 +++ got/got.1 @@ -159,15 +159,13 @@ If this directory is a .Nm work tree, use the repository path associated with this work tree. .El -.It Cm diff [ Fl C Ar number ] [ Ar repository-path ] Ar object1 Ar object2 -Display the differences between two objects in the repository. -Each +.It Cm diff [ Fl C Ar number ] [ Fl r Ar repository-path ] [ Ar object1 Ar object2 ] +Display differences between blobs in the repository and files in the +work tree, or between two specified objects in a repository. +If specified, each .Ar object argument is a SHA1 hash which corresponds to the object. Both objects must be of the same type (blobs, trees, or commits). -If the -.Ar repository path -is omitted, use the current working directory. .Pp The options for .Cm got diff @@ -176,6 +174,13 @@ are as follows: .It Fl C Ar number Set the number of context lines shown in the diff. By default, 3 lines of context are shown. +.It Fl r Ar repository-path +Use the repository at the specified path. +If not specified, assume the repository is located at or above the current +working directory. +If this directory is a +.Nm +work tree, use the repository path associated with this work tree. .El .It Cm blame [ Fl c Ar commit ] [ Fl r Ar repository-path ] Ar path Display line-by-line history of a file at the specified path. blob - d7e55d3b7bbc5195d9c895b68af79a5dd25b7e1e blob + 096c8f7fac1d8b3fd9d1ea2bc25096b07b9ee4ae --- got/got.c +++ got/got.c @@ -939,17 +939,69 @@ done: __dead static void usage_diff(void) { - fprintf(stderr, "usage: %s diff [-C number] [repository-path] " - "object1 object2\n", getprogname()); + fprintf(stderr, "usage: %s diff [-C number] [-r repository-path] " + "[object1 object2]\n", getprogname()); exit(1); } +struct print_diff_arg { + struct got_repository *repo; + struct got_worktree *worktree; + int diff_context; +}; + static const struct got_error * +print_diff(void *arg, unsigned char status, const char *path, + struct got_object_id *id) +{ + struct print_diff_arg *a = arg; + const struct got_error *err = NULL; + struct got_blob_object *blob1 = NULL; + FILE *f2 = NULL; + char *abspath = NULL; + struct stat sb; + + if (status != GOT_STATUS_MODIFIY) + return NULL; + + err = got_object_open_as_blob(&blob1, a->repo, id, 8192); + if (err) + goto done; + + if (asprintf(&abspath, "%s/%s", + got_worktree_get_root_path(a->worktree), path) == -1) { + err = got_error_from_errno(); + goto done; + } + + f2 = fopen(abspath, "r"); + if (f2 == NULL) { + err = got_error_from_errno(); + goto done; + } + if (lstat(abspath, &sb) == -1) { + err = got_error_from_errno(); + goto done; + } + + err = got_diff_blob_file(blob1, f2, sb.st_size, path, a->diff_context, + stdout); +done: + if (blob1) + got_object_blob_close(blob1); + if (f2) + fclose(f2); + free(abspath); + return err; +} + +static const struct got_error * cmd_diff(int argc, char *argv[]) { const struct got_error *error; struct got_repository *repo = NULL; - char *repo_path = NULL; + struct got_worktree *worktree = NULL; + char *cwd = NULL, *repo_path = NULL; struct got_object_id *id1 = NULL, *id2 = NULL; char *id_str1 = NULL, *id_str2 = NULL; int type1, type2; @@ -962,13 +1014,18 @@ cmd_diff(int argc, char *argv[]) err(1, "pledge"); #endif - while ((ch = getopt(argc, argv, "C:")) != -1) { + while ((ch = getopt(argc, argv, "C:r:")) != -1) { switch (ch) { case 'C': diff_context = strtonum(optarg, 1, INT_MAX, &errstr); if (errstr != NULL) err(1, "-C option %s", errstr); break; + case 'r': + repo_path = realpath(optarg, NULL); + if (repo_path == NULL) + err(1, "-r option"); + break; default: usage(); /* NOTREACHED */ @@ -978,24 +1035,35 @@ cmd_diff(int argc, char *argv[]) argc -= optind; argv += optind; + cwd = getcwd(NULL, 0); + if (cwd == NULL) { + error = got_error_from_errno(); + goto done; + } if (argc == 0) { - usage_diff(); /* TODO show local worktree changes */ - } else if (argc == 2) { - repo_path = getcwd(NULL, 0); + if (repo_path) + errx(1, + "-r option can't be used when diffing a work tree"); + error = got_worktree_open(&worktree, cwd); + if (error) + goto done; + repo_path = strdup(got_worktree_get_repo_path(worktree)); if (repo_path == NULL) return got_error_from_errno(); + } else if (argc == 2) { id_str1 = argv[0]; id_str2 = argv[1]; - } else if (argc == 3) { - repo_path = realpath(argv[0], NULL); - if (repo_path == NULL) - return got_error_from_errno(); - id_str1 = argv[1]; - id_str2 = argv[2]; } else usage_diff(); - error = apply_unveil(repo_path, NULL); + if (repo_path == NULL) { + repo_path = getcwd(NULL, 0); + if (repo_path == NULL) + return got_error_from_errno(); + } + + error = apply_unveil(repo_path, + worktree ? got_worktree_get_root_path(worktree) : NULL); if (error) goto done; @@ -1004,6 +1072,25 @@ cmd_diff(int argc, char *argv[]) if (error != NULL) goto done; + if (worktree) { + struct print_diff_arg arg; + char *id_str; + error = got_object_id_str(&id_str, + got_worktree_get_base_commit_id(worktree)); + if (error) + goto done; + arg.repo = repo; + arg.worktree = worktree; + arg.diff_context = diff_context; + + printf("diff %s %s\n", id_str, + got_worktree_get_root_path(worktree)); + error = got_worktree_status(worktree, repo, print_diff, + &arg, check_cancelled, NULL); + free(id_str); + goto done; + } + error = got_object_resolve_id_str(&id1, repo, id_str1); if (error) goto done; @@ -1047,6 +1134,8 @@ cmd_diff(int argc, char *argv[]) done: free(id1); free(id2); + if (worktree) + got_worktree_close(worktree); if (repo) { const struct got_error *repo_error; repo_error = got_repo_close(repo); @@ -1432,10 +1521,12 @@ usage_status(void) exit(1); } -static void -print_status(void *arg, unsigned char status, const char *path) +static const struct got_error * +print_status(void *arg, unsigned char status, const char *path, + struct got_object_id *id) { printf("%c %s\n", status, path); + return NULL; } static const struct got_error * blob - 01e0ac643704c52d2defe4e6e2bffdc0b8b5f8e4 blob + f80b6b9455be8bea47ca7815749d16720a267a51 --- include/got_diff.h +++ include/got_diff.h @@ -25,6 +25,16 @@ const struct got_error *got_diff_blob(struct got_blob_ struct got_blob_object *, const char *, const char *, int, FILE *); /* + * Compute the differences between a blob and a file and write unified diff + * text to the provided output file. The file's size must be provided, as + * well as a const char * diff header label which identifies the file. + * The number of context lines to show in the diff must be specified as well. + */ +const struct got_error * +got_diff_blob_file(struct got_blob_object *, FILE *, size_t, const char *, int, + FILE *); + +/* * Compute the differences between two trees and write unified diff text * to the provided output FILE. Two const char * diff header labels may * be provided which will be used to identify each blob in the diff output. blob - 768ca6a234213e1f696abed31c0a4266f2e2e612 blob + 7f9026a9b8d5c96a24479753a750d7e3d7ab7eec --- include/got_worktree.h +++ include/got_worktree.h @@ -83,7 +83,7 @@ struct got_reference *got_worktree_get_head_ref(struct /* * Get the current base commit ID of a worktree. */ -const struct got_object_id *got_worktree_get_base_commit_id(struct got_worktree *); +struct got_object_id *got_worktree_get_base_commit_id(struct got_worktree *); /* * Set the base commit Id of a worktree. @@ -111,7 +111,8 @@ const struct got_error *got_worktree_checkout_files(st got_worktree_cancel_cb, void *); /* A callback function which is invoked to report a path's status. */ -typedef void (*got_worktree_status_cb)(void *, unsigned char, const char *); +typedef const struct got_error *(*got_worktree_status_cb)(void *, + unsigned char, const char *, struct got_object_id *); /* * Report the status of paths in the work tree. blob - c89351f48f0b6b7aad4de60bfc866f18b5f6fd63 blob + a3da580ba00997c724ca5eb3cd2126aab7442ebd --- lib/diff.c +++ lib/diff.c @@ -126,6 +126,63 @@ got_diff_blob(struct got_blob_object *blob1, struct go } const struct got_error * +got_diff_blob_file(struct got_blob_object *blob1, FILE *f2, size_t size2, + const char *label2, int diff_context, FILE *outfile) +{ + struct got_diff_state ds; + struct got_diff_args args; + const struct got_error *err = NULL; + FILE *f1 = NULL; + char hex1[SHA1_DIGEST_STRING_LENGTH]; + char *idstr1 = NULL; + size_t size1; + int res, flags = 0; + + size1 = 0; + if (blob1) { + f1 = got_opentemp(); + if (f1 == NULL) + return got_error_from_errno(); + idstr1 = got_object_blob_id_str(blob1, hex1, sizeof(hex1)); + err = got_object_blob_dump_to_file(&size1, NULL, f1, blob1); + if (err) + goto done; + } else { + flags |= D_EMPTY1; + idstr1 = "/dev/null"; + } + + if (f2 == NULL) + flags |= D_EMPTY2; + + memset(&ds, 0, sizeof(ds)); + /* XXX should stat buffers be passed in args instead of ds? */ + ds.stb1.st_mode = S_IFREG; + if (blob1) + ds.stb1.st_size = size1; + ds.stb1.st_mtime = 0; /* XXX */ + + ds.stb2.st_mode = S_IFREG; + ds.stb2.st_size = size2; + ds.stb2.st_mtime = 0; /* XXX */ + + memset(&args, 0, sizeof(args)); + args.diff_format = D_UNIFIED; + args.label[0] = label2; + args.label[1] = label2; + args.diff_context = diff_context; + flags |= D_PROTOTYPE; + + fprintf(outfile, "blob - %s\n", idstr1); + fprintf(outfile, "file + %s\n", label2); + err = got_diffreg(&res, f1, f2, flags, &args, &ds, outfile, NULL); +done: + if (f1) + fclose(f1); + return err; +} + +const struct got_error * got_diff_blob_lines_changed(struct got_diff_changes **changes, struct got_blob_object *blob1, struct got_blob_object *blob2) { blob - 708cf625a91485cd69e6b44a4ed2699c28850ec7 blob + c0ccc0d1604c7cfb8c922ce2a76a686a2443fc8d --- lib/worktree.c +++ lib/worktree.c @@ -470,7 +470,7 @@ got_worktree_get_head_ref(struct got_worktree *worktre return got_ref_dup(worktree->head_ref); } -const struct got_object_id * +struct got_object_id * got_worktree_get_base_commit_id(struct got_worktree *worktree) { return worktree->base_commit_id; @@ -1039,8 +1039,11 @@ status_old_new(void *arg, struct got_fileindex_entry * } err = get_file_status(&status, ie, abspath, a->repo); - if (err == NULL && status != GOT_STATUS_NO_CHANGE) - (*a->status_cb)(a->status_arg, status, ie->path); + if (err == NULL && status != GOT_STATUS_NO_CHANGE) { + struct got_object_id id; + memcpy(id.sha1, ie->blob_sha1, SHA1_DIGEST_LENGTH); + err = (*a->status_cb)(a->status_arg, status, ie->path, &id); + } free(abspath); return err; } @@ -1049,13 +1052,16 @@ static const struct got_error * status_old(void *arg, struct got_fileindex_entry *ie, const char *parent_path) { struct diff_dir_cb_arg *a = arg; - (*a->status_cb)(a->status_arg, GOT_STATUS_MISSING, ie->path); - return NULL; + struct got_object_id id; + memcpy(id.sha1, ie->blob_sha1, SHA1_DIGEST_LENGTH); + return (*a->status_cb)(a->status_arg, GOT_STATUS_MISSING, ie->path, + &id); } static const struct got_error * status_new(void *arg, struct dirent *de, const char *parent_path) { + const struct got_error *err = NULL; struct diff_dir_cb_arg *a = arg; char *path = NULL; @@ -1069,10 +1075,11 @@ status_new(void *arg, struct dirent *de, const char *p path = de->d_name; } - (*a->status_cb)(a->status_arg, GOT_STATUS_UNVERSIONED, path); + err = (*a->status_cb)(a->status_arg, GOT_STATUS_UNVERSIONED, path, + NULL); if (parent_path[0]) free(path); - return NULL; + return err; } const struct got_error *