Commit Diff


commit - cd2a3010322415cd187fe81189a32e49cac09672
commit + e11883f02b06fcd5d68da0aa0b781f93b3e94568
blob - 0f8f83ded2fa834467a0c0bef5de29b3578ddcaa
blob + 6e0e4df78865c58469ef88303ebfa3222886178f
--- tog/tog.1
+++ tog/tog.1
@@ -455,6 +455,10 @@ view, move to the Nth next (older) commit.
 If the diff was opened via the
 .Cm blame
 view, move to the Nth next line and load the corresponding commit (default: 1).
+.It Cm p
+Write the currently viewed diff to a patch file in
+.Pa /tmp .
+The patch pathname is drawn to the status line.
 .It Cm /
 Prompt for a search pattern and start searching for matching lines.
 The search pattern is an extended regular expression.
blob - a8635df0a7f0dd6ef557f5328f6569374410b361
blob + 0f0c5142c308899d47b16880bf3b26d868b72c92
--- tog/tog.c
+++ tog/tog.c
@@ -336,6 +336,7 @@ get_color_value(const char *envvar)
 struct tog_diff_view_state {
 	struct got_object_id *id1, *id2;
 	const char *label1, *label2;
+	char *action;
 	FILE *f, *f1, *f2;
 	int fd1, fd2;
 	int lineno;
@@ -585,6 +586,7 @@ struct tog_help_view_state {
 	KEY_("A", "Toggle between Myers and Patience diff algorithm"), \
 	KEY_("a", "Toggle treatment of file as ASCII irrespective of binary" \
 	    " data"), \
+	KEY_("p", "Write diff to a patch file in /tmp"), \
 	KEY_("(", "Go to the previous file in the diff"), \
 	KEY_(")", "Go to the next file in the diff"), \
 	KEY_("{", "Go to the previous hunk in the diff"), \
@@ -1620,10 +1622,13 @@ action_report(struct tog_view *view)
 	/*
 	 * Clear action status report. Only clear in blame view
 	 * once annotating is complete, otherwise it's too fast.
+	 * In diff view, let its state control view->action lifetime.
 	 */
 	if (view->type == TOG_VIEW_BLAME) {
 		if (view->state.blame.blame_complete)
 			view->action = NULL;
+	} else if (view->type == TOG_VIEW_DIFF) {
+		view->action = view->state.diff.action;
 	} else
 		view->action = NULL;
 }
@@ -5611,6 +5616,8 @@ close_diff_view(struct tog_view *view)
 	s->id1 = NULL;
 	free(s->id2);
 	s->id2 = NULL;
+	free(s->action);
+	s->action = NULL;
 	if (s->f && fclose(s->f) == EOF)
 		err = got_error_from_errno("fclose");
 	s->f = NULL;
@@ -5806,6 +5813,67 @@ show_diff_view(struct tog_view *view)
 }
 
 static const struct got_error *
+diff_write_patch(struct tog_view *view)
+{
+	const struct got_error		*err;
+	struct tog_diff_view_state	*s = &view->state.diff;
+	FILE				*f;
+	char				 buf[BUFSIZ];
+	char				*path;
+	size_t				 r;
+	off_t				 pos;
+
+	if (s->action != NULL) {
+		free(s->action);
+		s->action = NULL;
+	}
+
+	pos = ftello(s->f);
+	if (pos == -1)
+		return got_error_from_errno("ftello");
+	if (fseeko(s->f, 0L, SEEK_SET) == -1)
+		return got_error_from_errno("fseeko");
+
+	err = got_opentemp_named(&path, &f, GOT_TMPDIR_STR "/tog", ".diff");
+	if (err != NULL)
+		return err;
+
+	while ((r = fread(buf, 1, sizeof(buf), s->f)) > 0) {
+		if (fwrite(buf, 1, r, f) != r) {
+			err = got_ferror(f, GOT_ERR_IO);
+			goto done;
+		}
+	}
+
+	if (ferror(s->f)) {
+		err = got_error_from_errno("fread");
+		goto done;
+	}
+	if (fseeko(s->f, pos, SEEK_SET) == -1) {
+		err = got_error_from_errno("fseeko");
+		goto done;
+	}
+
+	if (fflush(f) == EOF) {
+		err = got_error_from_errno2("fflush", path);
+		goto done;
+	}
+
+	if (asprintf(&s->action, "patch file written to %s", path) == -1) {
+		err = got_error_from_errno("asprintf");
+		goto done;
+	}
+
+	view->action = s->action;
+
+done:
+	if (f != NULL && fclose(f) == EOF && err == NULL)
+		err = got_error_from_errno2("fclose", path);
+	free(path);
+	return err;
+}
+
+static const struct got_error *
 set_selected_commit(struct tog_diff_view_state *s,
     struct commit_queue_entry *entry)
 {
@@ -5840,6 +5908,10 @@ reset_diff_view(struct tog_view *view)
 	s->first_displayed_line = 1;
 	s->last_displayed_line = view->nlines;
 	s->matched_line = 0;
+	if (s->action != NULL) {
+		free(s->action);
+		s->action = NULL;
+	}
 	diff_view_indicate_progress(view);
 	return create_diff(s);
 }
@@ -5899,6 +5971,12 @@ input_diff_view(struct tog_view **new_view, struct tog
 
 	s->lineno = s->first_displayed_line - 1 + s->selected_line;
 
+	if (s->action != NULL && ch != ERR) {
+		free(s->action);
+		s->action = NULL;
+		view->action = NULL;
+	}
+
 	switch (ch) {
 	case '0':
 	case '$':
@@ -6098,6 +6176,9 @@ input_diff_view(struct tog_view **new_view, struct tog
 
 		diff_view_indicate_progress(view);
 		err = create_diff(s);
+		break;
+	case 'p':
+		err = diff_write_patch(view);
 		break;
 	default:
 		view->count = 0;