2 * Copyright (c) 2022 Omar Polo <op@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 * Things that we may want to support:
19 * + support indented patches?
20 * + support other kinds of patches?
22 #include "got_compat.h"
24 #include <sys/types.h>
25 #include <sys/queue.h>
26 #include <sys/socket.h>
39 #include "got_error.h"
40 #include "got_object.h"
42 #include "got_reference.h"
43 #include "got_cancel.h"
44 #include "got_worktree.h"
45 #include "got_repository.h"
46 #include "got_opentemp.h"
47 #include "got_patch.h"
50 #include "got_lib_delta.h"
51 #include "got_lib_diff.h"
52 #include "got_lib_hash.h"
53 #include "got_lib_object.h"
54 #include "got_lib_privsep.h"
57 #define MIN(a, b) ((a) < (b) ? (a) : (b))
60 struct got_patch_line
{
66 struct got_patch_hunk
{
67 STAILQ_ENTRY(got_patch_hunk
) entries
;
68 const struct got_error
*err
;
79 struct got_patch_line
*lines
;
82 STAILQ_HEAD(got_patch_hunk_head
, got_patch_hunk
);
87 char cid
[GOT_HASH_DIGEST_STRING_MAXLEN
];
88 char blob
[GOT_HASH_DIGEST_STRING_MAXLEN
];
89 struct got_patch_hunk_head head
;
93 got_patch_progress_cb progress_cb
;
95 struct got_patch_hunk_head
*head
;
99 apply_umask(mode_t mode
)
108 static const struct got_error
*
109 send_patch(struct imsgbuf
*ibuf
, int fd
)
111 const struct got_error
*err
= NULL
;
113 if (imsg_compose(ibuf
, GOT_IMSG_PATCH_FILE
, 0, 0, fd
,
115 err
= got_error_from_errno(
116 "imsg_compose GOT_IMSG_PATCH_FILE");
121 return got_privsep_flush_imsg(ibuf
);
125 patch_free(struct got_patch
*p
)
127 struct got_patch_hunk
*h
;
130 while (!STAILQ_EMPTY(&p
->head
)) {
131 h
= STAILQ_FIRST(&p
->head
);
132 STAILQ_REMOVE_HEAD(&p
->head
, entries
);
134 for (i
= 0; i
< h
->len
; ++i
)
135 free(h
->lines
[i
].line
);
143 memset(p
, 0, sizeof(*p
));
144 STAILQ_INIT(&p
->head
);
147 static const struct got_error
*
148 pushline(struct got_patch_hunk
*h
, const char *line
, size_t len
)
153 if (h
->len
== h
->cap
) {
154 if ((newcap
= h
->cap
* 1.5) == 0)
156 t
= recallocarray(h
->lines
, h
->cap
, newcap
,
157 sizeof(h
->lines
[0]));
159 return got_error_from_errno("recallocarray");
164 if ((t
= malloc(len
- 1)) == NULL
)
165 return got_error_from_errno("malloc");
166 memcpy(t
, line
+ 1, len
- 1); /* skip the line type */
168 h
->lines
[h
->len
].mode
= *line
;
169 h
->lines
[h
->len
].line
= t
;
170 h
->lines
[h
->len
].len
= len
- 2; /* line type and trailing NUL */
175 static const struct got_error
*
176 recv_patch(struct imsgbuf
*ibuf
, int *done
, struct got_patch
*p
, int strip
)
178 const struct got_error
*err
= NULL
;
180 struct got_imsg_patch_hunk hdr
;
181 struct got_imsg_patch patch
;
182 struct got_patch_hunk
*h
= NULL
;
186 memset(p
, 0, sizeof(*p
));
187 STAILQ_INIT(&p
->head
);
189 err
= got_privsep_recv_imsg(&imsg
, ibuf
, 0);
192 if (imsg
.hdr
.type
== GOT_IMSG_PATCH_EOF
) {
196 if (imsg
.hdr
.type
!= GOT_IMSG_PATCH
) {
197 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
200 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
201 if (datalen
!= sizeof(patch
)) {
202 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
205 memcpy(&patch
, imsg
.data
, sizeof(patch
));
207 if (patch
.old
[sizeof(patch
.old
)-1] != '\0' ||
208 patch
.new[sizeof(patch
.new)-1] != '\0' ||
209 patch
.cid
[sizeof(patch
.cid
)-1] != '\0' ||
210 patch
.blob
[sizeof(patch
.blob
)-1] != '\0') {
211 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
215 if (*patch
.cid
!= '\0')
216 strlcpy(p
->cid
, patch
.cid
, sizeof(p
->cid
));
218 if (*patch
.blob
!= '\0')
219 strlcpy(p
->blob
, patch
.blob
, sizeof(p
->blob
));
221 p
->xbit
= patch
.xbit
;
223 /* automatically set strip=1 for git-style diffs */
224 if (strip
== -1 && patch
.git
&&
225 (*patch
.old
== '\0' || !strncmp(patch
.old
, "a/", 2)) &&
226 (*patch
.new == '\0' || !strncmp(patch
.new, "b/", 2)))
229 /* prefer the new name if not /dev/null for not git-style diffs */
230 if (!patch
.git
&& *patch
.new != '\0' && *patch
.old
!= '\0') {
231 err
= got_path_strip(&p
->old
, patch
.new, strip
);
234 } else if (*patch
.old
!= '\0') {
235 err
= got_path_strip(&p
->old
, patch
.old
, strip
);
240 if (*patch
.new != '\0') {
241 err
= got_path_strip(&p
->new, patch
.new, strip
);
246 if (p
->old
== NULL
&& p
->new == NULL
) {
247 err
= got_error(GOT_ERR_PATCH_MALFORMED
);
256 err
= got_privsep_recv_imsg(&imsg
, ibuf
, 0);
262 datalen
= imsg
.hdr
.len
- IMSG_HEADER_SIZE
;
263 switch (imsg
.hdr
.type
) {
264 case GOT_IMSG_PATCH_DONE
:
265 if (h
!= NULL
&& h
->len
== 0)
266 err
= got_error(GOT_ERR_PATCH_MALFORMED
);
268 case GOT_IMSG_PATCH_HUNK
:
270 (h
->len
== 0 || h
->old_nonl
|| h
->new_nonl
)) {
271 err
= got_error(GOT_ERR_PATCH_MALFORMED
);
275 if (datalen
!= sizeof(hdr
)) {
276 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
279 memcpy(&hdr
, imsg
.data
, sizeof(hdr
));
280 if (hdr
.oldfrom
< 0 || hdr
.newfrom
< 0) {
281 err
= got_error(GOT_ERR_PRIVSEP_LEN
);
284 if ((h
= calloc(1, sizeof(*h
))) == NULL
) {
285 err
= got_error_from_errno("calloc");
288 h
->old_from
= hdr
.oldfrom
;
289 h
->old_lines
= hdr
.oldlines
;
290 h
->new_from
= hdr
.newfrom
;
291 h
->new_lines
= hdr
.newlines
;
292 STAILQ_INSERT_TAIL(&p
->head
, h
, entries
);
294 case GOT_IMSG_PATCH_LINE
:
296 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
300 /* at least one char */
301 if (datalen
< 2 || t
[datalen
-1] != '\0') {
302 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
305 if (*t
!= ' ' && *t
!= '-' && *t
!= '+' &&
307 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
312 err
= pushline(h
, t
, datalen
);
313 else if (lastmode
== '-')
315 else if (lastmode
== '+')
318 err
= got_error(GOT_ERR_PATCH_MALFORMED
);
326 err
= got_error(GOT_ERR_PRIVSEP_MSG
);
342 reverse_patch(struct got_patch
*p
)
344 struct got_patch_hunk
*h
;
348 STAILQ_FOREACH(h
, &p
->head
, entries
) {
350 h
->old_from
= h
->new_from
;
354 h
->old_lines
= h
->new_lines
;
358 h
->old_nonl
= h
->new_nonl
;
361 for (i
= 0; i
< h
->len
; ++i
) {
362 if (h
->lines
[i
].mode
== '+')
363 h
->lines
[i
].mode
= '-';
364 else if (h
->lines
[i
].mode
== '-')
365 h
->lines
[i
].mode
= '+';
371 * Copy data from orig starting at copypos until pos into tmp.
372 * If pos is -1, copy until EOF.
374 static const struct got_error
*
375 copy(FILE *tmp
, FILE *orig
, off_t copypos
, off_t pos
)
380 if (fseeko(orig
, copypos
, SEEK_SET
) == -1)
381 return got_error_from_errno("fseeko");
383 while (pos
== -1 || copypos
< pos
) {
386 len
= MIN(len
, (size_t)pos
- copypos
);
387 r
= fread(buf
, 1, len
, orig
);
388 if (r
!= len
&& ferror(orig
))
389 return got_error_from_errno("fread");
390 w
= fwrite(buf
, 1, r
, tmp
);
392 return got_error_from_errno("fwrite");
394 if (r
!= len
&& feof(orig
)) {
397 return got_error(GOT_ERR_HUNK_FAILED
);
403 static int lines_eq(struct got_patch_line
*, const char *, size_t, int *);
405 static const struct got_error
*
406 locate_hunk(FILE *orig
, struct got_patch_hunk
*h
, off_t
*pos
, int *lineno
)
408 const struct got_error
*err
= NULL
;
409 struct got_patch_line
*l
= &h
->lines
[0];
415 int mangled
= 0, match_lineno
= -1;
419 linelen
= getline(&line
, &linesize
, orig
);
422 err
= got_error_from_errno("getline");
423 /* An EOF is fine iff the target file is empty. */
424 if (feof(orig
) && match
== -1 && h
->old_lines
!= 0)
425 err
= got_error(GOT_ERR_HUNK_FAILED
);
427 match_lineno
= (*lineno
)-1;
431 if ((mode
== ' ' && lines_eq(l
, line
, linelen
, &mangled
)) ||
432 (mode
== '-' && lines_eq(l
, line
, linelen
, &mangled
)) ||
433 (mode
== '+' && *lineno
== h
->old_from
)) {
434 match
= ftello(orig
);
436 err
= got_error_from_errno("ftello");
440 match_lineno
= (*lineno
)-1;
443 if (*lineno
>= h
->old_from
&& match
!= -1) {
452 *lineno
= match_lineno
;
453 if (fseeko(orig
, match
, SEEK_SET
) == -1)
454 err
= got_error_from_errno("fseeko");
462 lines_eq(struct got_patch_line
*l
, const char *b
, size_t len
, int *mangled
)
467 if (len
> 00 && b
[len
- 1] == '\n')
471 if (l
->len
== len
&& !memcmp(a
, b
, len
))
479 (a
[i
] == '\t' || a
[i
] == ' ' || a
[i
] == '\f'))
482 (b
[j
] == '\t' || b
[j
] == ' ' || b
[j
] == '\f'))
484 if (i
== l
->len
|| j
== len
|| a
[i
] != b
[j
])
489 return (i
== l
->len
&& j
== len
);
492 static const struct got_error
*
493 test_hunk(FILE *orig
, struct got_patch_hunk
*h
)
495 const struct got_error
*err
= NULL
;
497 size_t linesize
= 0, i
= 0;
501 for (i
= 0; i
< h
->len
; ++i
) {
502 switch (h
->lines
[i
].mode
) {
507 linelen
= getline(&line
, &linesize
, orig
);
510 err
= got_error_from_errno("getline");
513 GOT_ERR_HUNK_FAILED
);
516 if (!lines_eq(&h
->lines
[i
], line
, linelen
, &mangled
)) {
517 err
= got_error(GOT_ERR_HUNK_FAILED
);
531 static const struct got_error
*
532 apply_hunk(FILE *orig
, FILE *tmp
, struct got_patch_hunk
*h
, int *lineno
,
535 const struct got_error
*err
= NULL
;
537 size_t linesize
= 0, i
, new = 0;
543 if (orig
!= NULL
&& fseeko(orig
, from
, SEEK_SET
) == -1)
544 return got_error_from_errno("fseeko");
546 for (i
= 0; i
< h
->len
; ++i
) {
547 switch (mode
= h
->lines
[i
].mode
) {
552 linelen
= getline(&line
, &linesize
, orig
);
554 err
= got_error_from_errno("getline");
557 if (line
[linelen
- 1] == '\n')
558 line
[linelen
- 1] = '\0';
562 t
= h
->lines
[i
].line
;
567 if (fwrite(t
, 1, l
, tmp
) != l
||
568 fputc('\n', tmp
) == EOF
) {
569 err
= got_error_from_errno("fprintf");
575 t
= h
->lines
[i
].line
;
577 if (fwrite(t
, 1, l
, tmp
) != l
) {
578 err
= got_error_from_errno("fprintf");
581 if (new != h
->new_lines
|| !h
->new_nonl
) {
582 if (fprintf(tmp
, "\n") < 0) {
583 err
= got_error_from_errno("fprintf");
596 static const struct got_error
*
597 patch_file(struct got_patch
*p
, FILE *orig
, FILE *tmp
)
599 const struct got_error
*err
= NULL
;
600 struct got_patch_hunk
*h
;
608 if (p
->old
== NULL
) { /* create */
609 h
= STAILQ_FIRST(&p
->head
);
610 if (h
== NULL
|| STAILQ_NEXT(h
, entries
) != NULL
)
611 return got_error(GOT_ERR_PATCH_MALFORMED
);
612 return apply_hunk(orig
, tmp
, h
, &lineno
, 0);
615 /* When deleting binary files there are no hunks to apply. */
616 if (p
->new == NULL
&& STAILQ_EMPTY(&p
->head
))
619 if (fstat(fileno(orig
), &sb
) == -1)
620 return got_error_from_errno("fstat");
623 STAILQ_FOREACH(h
, &p
->head
, entries
) {
625 err
= locate_hunk(orig
, h
, &pos
, &lineno
);
626 if (err
!= NULL
&& err
->code
== GOT_ERR_HUNK_FAILED
)
630 err
= copy(tmp
, orig
, copypos
, pos
);
635 err
= test_hunk(orig
, h
);
636 if (err
!= NULL
&& err
->code
== GOT_ERR_HUNK_FAILED
) {
638 * try to apply the hunk again starting the search
639 * after the previous partial match.
641 if (fseeko(orig
, pos
, SEEK_SET
) == -1)
642 return got_error_from_errno("fseeko");
643 linelen
= getline(&line
, &linesize
, orig
);
645 return got_error_from_errno("getline");
652 if (lineno
+ 1 != h
->old_from
)
653 h
->offset
= lineno
+ 1 - h
->old_from
;
655 err
= apply_hunk(orig
, tmp
, h
, &lineno
, pos
);
659 copypos
= ftello(orig
);
661 return got_error_from_errno("ftello");
664 if (p
->new == NULL
&& sb
.st_size
!= copypos
) {
665 h
= STAILQ_FIRST(&p
->head
);
666 h
->err
= got_error(GOT_ERR_HUNK_FAILED
);
668 } else if (!feof(orig
))
669 err
= copy(tmp
, orig
, copypos
, -1);
674 static const struct got_error
*
675 report_progress(struct patch_args
*pa
, const char *old
, const char *new,
676 unsigned char status
, const struct got_error
*orig_error
)
678 const struct got_error
*err
;
679 struct got_patch_hunk
*h
;
681 err
= pa
->progress_cb(pa
->progress_arg
, old
, new, status
,
682 orig_error
, 0, 0, 0, 0, 0, 0, NULL
);
686 STAILQ_FOREACH(h
, pa
->head
, entries
) {
687 if (h
->offset
== 0 && !h
->ws_mangled
&& h
->err
== NULL
)
690 err
= pa
->progress_cb(pa
->progress_arg
, old
, new, 0, NULL
,
691 h
->old_from
, h
->old_lines
, h
->new_from
, h
->new_lines
,
692 h
->offset
, h
->ws_mangled
, h
->err
);
700 static const struct got_error
*
701 patch_delete(void *arg
, unsigned char status
, unsigned char staged_status
,
704 return report_progress(arg
, path
, NULL
, status
, NULL
);
707 static const struct got_error
*
708 patch_add(void *arg
, unsigned char status
, const char *path
)
710 return report_progress(arg
, NULL
, path
, status
, NULL
);
713 static const struct got_error
*
714 open_blob(char **path
, FILE **fp
, const char *blobid
,
715 struct got_repository
*repo
)
717 const struct got_error
*err
= NULL
;
718 struct got_blob_object
*blob
= NULL
;
719 struct got_object_id id
, *idptr
, *matched_id
= NULL
;
720 enum got_hash_algorithm algo
;
725 algo
= got_repo_get_object_format(repo
);
727 if (strlen(blobid
) != got_hash_digest_string_length(algo
) - 1) {
728 err
= got_repo_match_object_id(&matched_id
, NULL
, blobid
,
729 GOT_OBJ_TYPE_BLOB
, NULL
/* do not resolve tags */,
735 if (!got_parse_object_id(&id
, blobid
, algo
))
736 return got_error(GOT_ERR_BAD_OBJ_ID_STR
);
740 fd
= got_opentempfd();
742 err
= got_error_from_errno("got_opentempfd");
746 err
= got_object_open_as_blob(&blob
, repo
, idptr
, 8192, fd
);
750 err
= got_opentemp_named(path
, fp
, GOT_TMPDIR_STR
"/got-patch-blob",
755 err
= got_object_blob_dump_to_file(NULL
, NULL
, NULL
, *fp
, blob
);
760 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
761 err
= got_error_from_errno("close");
763 got_object_blob_close(blob
);
764 if (matched_id
!= NULL
)
778 static const struct got_error
*
779 prepare_merge(int *do_merge
, char **apath
, FILE **afile
,
780 struct got_worktree
*worktree
, struct got_repository
*repo
,
781 struct got_patch
*p
, struct got_object_id
*commit_id
,
782 struct got_tree_object
*tree
, const char *path
)
784 const struct got_error
*err
= NULL
;
790 /* don't run the diff3 merge on creations/deletions */
791 if (p
->old
== NULL
|| p
->new == NULL
)
795 struct got_object_id
*id
;
797 err
= got_object_tree_find_path(&id
, NULL
, repo
, tree
, path
);
800 got_object_id_hex(id
, p
->blob
, sizeof(p
->blob
));
801 got_object_id_hex(commit_id
, p
->cid
, sizeof(p
->cid
));
803 err
= open_blob(apath
, afile
, p
->blob
, repo
);
804 *do_merge
= err
== NULL
;
805 } else if (*p
->blob
!= '\0') {
806 err
= open_blob(apath
, afile
, p
->blob
, repo
);
808 * ignore failures to open this blob, we might have
811 if (err
&& !(err
->code
== GOT_ERR_ERRNO
&& errno
== ENOENT
) &&
812 err
->code
!= GOT_ERR_NO_OBJ
)
814 *do_merge
= err
== NULL
;
821 static const struct got_error
*
822 apply_patch(int *overlapcnt
, struct got_worktree
*worktree
,
823 struct got_repository
*repo
, struct got_fileindex
*fileindex
,
824 const char *old
, const char *new, struct got_patch
*p
, int nop
,
825 int reverse
, struct got_object_id
*commit_id
,
826 struct got_tree_object
*tree
, struct patch_args
*pa
,
827 got_cancel_cb cancel_cb
, void *cancel_arg
)
829 const struct got_error
*err
= NULL
;
831 int do_merge
= 0, file_renamed
= 0;
832 char *oldlabel
= NULL
, *newlabel
= NULL
, *anclabel
= NULL
;
833 char *oldpath
= NULL
, *newpath
= NULL
;
834 char *tmppath
= NULL
, *template = NULL
;
835 char *apath
= NULL
, *mergepath
= NULL
;
836 FILE *oldfile
= NULL
, *tmpfile
= NULL
, *afile
= NULL
, *mergefile
= NULL
;
838 mode_t mode
= GOT_DEFAULT_FILE_MODE
;
842 err
= prepare_merge(&do_merge
, &apath
, &afile
, worktree
, repo
, p
,
843 commit_id
, tree
, old
);
847 if (reverse
&& !do_merge
)
850 if (asprintf(&oldpath
, "%s/%s", got_worktree_get_root_path(worktree
),
852 err
= got_error_from_errno("asprintf");
856 if (asprintf(&newpath
, "%s/%s", got_worktree_get_root_path(worktree
),
858 err
= got_error_from_errno("asprintf");
862 file_renamed
= strcmp(oldpath
, newpath
);
864 if (asprintf(&template, "%s/got-patch",
865 got_worktree_get_root_path(worktree
)) == -1) {
866 err
= got_error_from_errno(template);
870 if (p
->old
!= NULL
) {
871 if ((oldfile
= fopen(oldpath
, "r")) == NULL
) {
872 err
= got_error_from_errno2("open", oldpath
);
875 if (fstat(fileno(oldfile
), &sb
) == -1) {
876 err
= got_error_from_errno2("fstat", oldpath
);
881 mode
|= (S_IXUSR
| S_IXGRP
| S_IXOTH
);
883 err
= got_opentemp_named(&tmppath
, &tmpfile
, template, "");
886 outfd
= fileno(tmpfile
);
887 err
= patch_file(p
, afile
!= NULL
? afile
: oldfile
, tmpfile
);
892 const char *type
, *id
;
894 if (fseeko(afile
, 0, SEEK_SET
) == -1 ||
895 fseeko(oldfile
, 0, SEEK_SET
) == -1 ||
896 fseeko(tmpfile
, 0, SEEK_SET
) == -1) {
897 err
= got_error_from_errno("fseeko");
901 if (asprintf(&oldlabel
, "--- %s", p
->old
) == -1) {
902 err
= got_error_from_errno("asprintf");
907 if (asprintf(&newlabel
, "+++ %s", p
->new) == -1) {
908 err
= got_error_from_errno("asprintf");
913 if (*p
->cid
!= '\0') {
921 if (asprintf(&anclabel
, "%s %s", type
, id
) == -1) {
922 err
= got_error_from_errno("asprintf");
940 err
= got_opentemp_named(&mergepath
, &mergefile
, template, "");
943 outfd
= fileno(mergefile
);
945 err
= got_merge_diff3(overlapcnt
, outfd
, tmpfile
, afile
,
946 oldfile
, oldlabel
, anclabel
, newlabel
,
947 GOT_DIFF_ALGORITHM_PATIENCE
);
955 if (p
->old
!= NULL
&& p
->new == NULL
) {
956 err
= got_worktree_patch_schedule_rm(old
, repo
, worktree
,
957 fileindex
, patch_delete
, pa
);
961 if (fchmod(outfd
, apply_umask(mode
)) == -1) {
962 err
= got_error_from_errno2("chmod", tmppath
);
967 err
= got_path_move_file(mergepath
, newpath
);
973 err
= got_path_move_file(tmppath
, newpath
);
981 err
= got_worktree_patch_schedule_rm(old
, repo
, worktree
,
982 fileindex
, patch_delete
, pa
);
984 err
= got_worktree_patch_schedule_add(new, repo
,
985 worktree
, fileindex
, patch_add
,
989 } else if (p
->old
== NULL
) {
990 err
= got_worktree_patch_schedule_add(new, repo
, worktree
,
991 fileindex
, patch_add
, pa
);
994 } else if (*overlapcnt
!= 0)
995 err
= report_progress(pa
, old
, new, GOT_STATUS_CONFLICT
, NULL
);
997 err
= report_progress(pa
, old
, new, GOT_STATUS_MERGE
, NULL
);
999 err
= report_progress(pa
, old
, new, GOT_STATUS_MODIFY
, NULL
);
1004 if (tmppath
!= NULL
&& unlink(tmppath
) == -1 && err
== NULL
)
1005 err
= got_error_from_errno("unlink");
1006 if (tmpfile
!= NULL
&& fclose(tmpfile
) == EOF
&& err
== NULL
)
1007 err
= got_error_from_errno("fclose");
1011 if (oldfile
!= NULL
&& fclose(oldfile
) == EOF
&& err
== NULL
)
1012 err
= got_error_from_errno("fclose");
1014 if (apath
!= NULL
&& unlink(apath
) == -1 && err
== NULL
)
1015 err
= got_error_from_errno("unlink");
1016 if (afile
!= NULL
&& fclose(afile
) == EOF
&& err
== NULL
)
1017 err
= got_error_from_errno("fclose");
1020 if (mergepath
!= NULL
&& unlink(mergepath
) == -1 && err
== NULL
)
1021 err
= got_error_from_errno("unlink");
1022 if (mergefile
!= NULL
&& fclose(mergefile
) == EOF
&& err
== NULL
)
1023 err
= got_error_from_errno("fclose");
1033 const struct got_error
*
1034 got_patch(int fd
, struct got_worktree
*worktree
, struct got_repository
*repo
,
1035 int nop
, int strip
, int reverse
, struct got_object_id
*commit_id
,
1036 got_patch_progress_cb progress_cb
, void *progress_arg
,
1037 got_cancel_cb cancel_cb
, void *cancel_arg
)
1039 const struct got_error
*err
= NULL
, *complete_err
= NULL
;
1040 struct got_fileindex
*fileindex
= NULL
;
1041 struct got_commit_object
*commit
= NULL
;
1042 struct got_tree_object
*tree
= NULL
;
1043 char *fileindex_path
= NULL
;
1044 char *oldpath
, *newpath
;
1045 struct imsgbuf
*ibuf
;
1046 int imsg_fds
[2] = {-1, -1};
1047 int overlapcnt
, done
= 0, failed
= 0;
1050 ibuf
= calloc(1, sizeof(*ibuf
));
1052 err
= got_error_from_errno("calloc");
1056 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, imsg_fds
) == -1) {
1057 err
= got_error_from_errno("socketpair");
1063 err
= got_error_from_errno("fork");
1065 } else if (pid
== 0) {
1066 got_privsep_exec_child(imsg_fds
, GOT_PATH_PROG_READ_PATCH
,
1071 if (close(imsg_fds
[1]) == -1) {
1072 err
= got_error_from_errno("close");
1076 if (imsgbuf_init(ibuf
, imsg_fds
[0]) == -1) {
1077 err
= got_error_from_errno("imsgbuf_init");
1080 imsgbuf_allow_fdpass(ibuf
);
1082 err
= send_patch(ibuf
, fd
);
1087 err
= got_worktree_patch_prepare(&fileindex
, &fileindex_path
,
1093 err
= got_object_open_as_commit(&commit
, repo
, commit_id
);
1097 err
= got_object_open_as_tree(&tree
, repo
, commit
->tree_id
);
1102 while (!done
&& err
== NULL
) {
1104 struct patch_args pa
;
1106 pa
.progress_cb
= progress_cb
;
1107 pa
.progress_arg
= progress_arg
;
1110 err
= recv_patch(ibuf
, &done
, &p
, strip
);
1114 err
= got_worktree_patch_check_path(p
.old
, p
.new, &oldpath
,
1115 &newpath
, worktree
, repo
, fileindex
);
1117 err
= apply_patch(&overlapcnt
, worktree
, repo
,
1118 fileindex
, oldpath
, newpath
, &p
, nop
, reverse
,
1119 commit_id
, tree
, &pa
, cancel_cb
, cancel_arg
);
1122 /* recoverable errors */
1123 if (err
->code
== GOT_ERR_FILE_STATUS
||
1124 (err
->code
== GOT_ERR_ERRNO
&& errno
== ENOENT
))
1125 err
= report_progress(&pa
, p
.old
, p
.new,
1126 GOT_STATUS_CANNOT_UPDATE
, err
);
1127 else if (err
->code
== GOT_ERR_HUNK_FAILED
)
1128 err
= report_progress(&pa
, p
.old
, p
.new,
1129 GOT_STATUS_CANNOT_UPDATE
, NULL
);
1131 if (overlapcnt
!= 0)
1143 complete_err
= got_worktree_patch_complete(worktree
, fileindex
,
1145 if (complete_err
&& err
== NULL
)
1147 free(fileindex_path
);
1149 got_object_tree_close(tree
);
1151 got_object_commit_close(commit
);
1152 if (fd
!= -1 && close(fd
) == -1 && err
== NULL
)
1153 err
= got_error_from_errno("close");
1155 imsgbuf_clear(ibuf
);
1156 if (imsg_fds
[0] != -1 && close(imsg_fds
[0]) == -1 && err
== NULL
)
1157 err
= got_error_from_errno("close");
1158 if (imsg_fds
[1] != -1 && close(imsg_fds
[1]) == -1 && err
== NULL
)
1159 err
= got_error_from_errno("close");
1160 if (err
== NULL
&& failed
)
1161 err
= got_error(GOT_ERR_PATCH_FAILED
);