Bug Summary

File:got/got.c
Warning:line 6726, column 11
Although the value stored to 'linelen' is used in the enclosing expression, the value is never actually read from 'linelen'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd6.9 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name got.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/local/lib/clang/11.1.0 -I /home/ben/Projects/got/got/../include -I /home/ben/Projects/got/got/../lib -D GOT_LIBEXECDIR=/home/ben/bin -D GOT_VERSION=0.53-current -internal-isystem /usr/local/lib/clang/11.1.0/include -internal-externc-isystem /usr/include -O0 -fdebug-compilation-dir /home/ben/Projects/got/got/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/got/scan/2021-05-28-230913-68537-1 -x c /home/ben/Projects/got/got/got.c
1/*
2 * Copyright (c) 2017 Martin Pieuchot <mpi@openbsd.org>
3 * Copyright (c) 2018, 2019, 2020 Stefan Sperling <stsp@openbsd.org>
4 * Copyright (c) 2020 Ori Bernstein <ori@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/queue.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <sys/param.h>
23#include <sys/wait.h>
24
25#include <err.h>
26#include <errno(*__errno()).h>
27#include <fcntl.h>
28#include <limits.h>
29#include <locale.h>
30#include <ctype.h>
31#include <signal.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <unistd.h>
36#include <libgen.h>
37#include <time.h>
38#include <paths.h>
39#include <regex.h>
40#include <getopt.h>
41#include <util.h>
42
43#include "got_version.h"
44#include "got_error.h"
45#include "got_object.h"
46#include "got_reference.h"
47#include "got_repository.h"
48#include "got_path.h"
49#include "got_cancel.h"
50#include "got_worktree.h"
51#include "got_diff.h"
52#include "got_commit_graph.h"
53#include "got_fetch.h"
54#include "got_blame.h"
55#include "got_privsep.h"
56#include "got_opentemp.h"
57#include "got_gotconfig.h"
58
59#ifndef nitems
60#define nitems(_a)(sizeof((_a)) / sizeof((_a)[0])) (sizeof((_a)) / sizeof((_a)[0]))
61#endif
62
63static volatile sig_atomic_t sigint_received;
64static volatile sig_atomic_t sigpipe_received;
65
66static void
67catch_sigint(int signo)
68{
69 sigint_received = 1;
70}
71
72static void
73catch_sigpipe(int signo)
74{
75 sigpipe_received = 1;
76}
77
78
79struct got_cmd {
80 const char *cmd_name;
81 const struct got_error *(*cmd_main)(int, char *[]);
82 void (*cmd_usage)(void);
83 const char *cmd_alias;
84};
85
86__dead__attribute__((__noreturn__)) static void usage(int, int);
87__dead__attribute__((__noreturn__)) static void usage_init(void);
88__dead__attribute__((__noreturn__)) static void usage_import(void);
89__dead__attribute__((__noreturn__)) static void usage_clone(void);
90__dead__attribute__((__noreturn__)) static void usage_fetch(void);
91__dead__attribute__((__noreturn__)) static void usage_checkout(void);
92__dead__attribute__((__noreturn__)) static void usage_update(void);
93__dead__attribute__((__noreturn__)) static void usage_log(void);
94__dead__attribute__((__noreturn__)) static void usage_diff(void);
95__dead__attribute__((__noreturn__)) static void usage_blame(void);
96__dead__attribute__((__noreturn__)) static void usage_tree(void);
97__dead__attribute__((__noreturn__)) static void usage_status(void);
98__dead__attribute__((__noreturn__)) static void usage_ref(void);
99__dead__attribute__((__noreturn__)) static void usage_branch(void);
100__dead__attribute__((__noreturn__)) static void usage_tag(void);
101__dead__attribute__((__noreturn__)) static void usage_add(void);
102__dead__attribute__((__noreturn__)) static void usage_remove(void);
103__dead__attribute__((__noreturn__)) static void usage_revert(void);
104__dead__attribute__((__noreturn__)) static void usage_commit(void);
105__dead__attribute__((__noreturn__)) static void usage_cherrypick(void);
106__dead__attribute__((__noreturn__)) static void usage_backout(void);
107__dead__attribute__((__noreturn__)) static void usage_rebase(void);
108__dead__attribute__((__noreturn__)) static void usage_histedit(void);
109__dead__attribute__((__noreturn__)) static void usage_integrate(void);
110__dead__attribute__((__noreturn__)) static void usage_stage(void);
111__dead__attribute__((__noreturn__)) static void usage_unstage(void);
112__dead__attribute__((__noreturn__)) static void usage_cat(void);
113__dead__attribute__((__noreturn__)) static void usage_info(void);
114
115static const struct got_error* cmd_init(int, char *[]);
116static const struct got_error* cmd_import(int, char *[]);
117static const struct got_error* cmd_clone(int, char *[]);
118static const struct got_error* cmd_fetch(int, char *[]);
119static const struct got_error* cmd_checkout(int, char *[]);
120static const struct got_error* cmd_update(int, char *[]);
121static const struct got_error* cmd_log(int, char *[]);
122static const struct got_error* cmd_diff(int, char *[]);
123static const struct got_error* cmd_blame(int, char *[]);
124static const struct got_error* cmd_tree(int, char *[]);
125static const struct got_error* cmd_status(int, char *[]);
126static const struct got_error* cmd_ref(int, char *[]);
127static const struct got_error* cmd_branch(int, char *[]);
128static const struct got_error* cmd_tag(int, char *[]);
129static const struct got_error* cmd_add(int, char *[]);
130static const struct got_error* cmd_remove(int, char *[]);
131static const struct got_error* cmd_revert(int, char *[]);
132static const struct got_error* cmd_commit(int, char *[]);
133static const struct got_error* cmd_cherrypick(int, char *[]);
134static const struct got_error* cmd_backout(int, char *[]);
135static const struct got_error* cmd_rebase(int, char *[]);
136static const struct got_error* cmd_histedit(int, char *[]);
137static const struct got_error* cmd_integrate(int, char *[]);
138static const struct got_error* cmd_stage(int, char *[]);
139static const struct got_error* cmd_unstage(int, char *[]);
140static const struct got_error* cmd_cat(int, char *[]);
141static const struct got_error* cmd_info(int, char *[]);
142
143static struct got_cmd got_commands[] = {
144 { "init", cmd_init, usage_init, "" },
145 { "import", cmd_import, usage_import, "im" },
146 { "clone", cmd_clone, usage_clone, "cl" },
147 { "fetch", cmd_fetch, usage_fetch, "fe" },
148 { "checkout", cmd_checkout, usage_checkout, "co" },
149 { "update", cmd_update, usage_update, "up" },
150 { "log", cmd_log, usage_log, "" },
151 { "diff", cmd_diff, usage_diff, "di" },
152 { "blame", cmd_blame, usage_blame, "bl" },
153 { "tree", cmd_tree, usage_tree, "tr" },
154 { "status", cmd_status, usage_status, "st" },
155 { "ref", cmd_ref, usage_ref, "" },
156 { "branch", cmd_branch, usage_branch, "br" },
157 { "tag", cmd_tag, usage_tag, "" },
158 { "add", cmd_add, usage_add, "" },
159 { "remove", cmd_remove, usage_remove, "rm" },
160 { "revert", cmd_revert, usage_revert, "rv" },
161 { "commit", cmd_commit, usage_commit, "ci" },
162 { "cherrypick", cmd_cherrypick, usage_cherrypick, "cy" },
163 { "backout", cmd_backout, usage_backout, "bo" },
164 { "rebase", cmd_rebase, usage_rebase, "rb" },
165 { "histedit", cmd_histedit, usage_histedit, "he" },
166 { "integrate", cmd_integrate, usage_integrate,"ig" },
167 { "stage", cmd_stage, usage_stage, "sg" },
168 { "unstage", cmd_unstage, usage_unstage, "ug" },
169 { "cat", cmd_cat, usage_cat, "" },
170 { "info", cmd_info, usage_info, "" },
171};
172
173static void
174list_commands(FILE *fp)
175{
176 size_t i;
177
178 fprintf(fp, "commands:");
179 for (i = 0; i < nitems(got_commands)(sizeof((got_commands)) / sizeof((got_commands)[0])); i++) {
180 struct got_cmd *cmd = &got_commands[i];
181 fprintf(fp, " %s", cmd->cmd_name);
182 }
183 fputc('\n', fp);
184}
185
186__dead__attribute__((__noreturn__)) static void
187option_conflict(char a, char b)
188{
189 errx(1, "-%c and -%c options are mutually exclusive", a, b);
190}
191
192int
193main(int argc, char *argv[])
194{
195 struct got_cmd *cmd;
196 size_t i;
197 int ch;
198 int hflag = 0, Vflag = 0;
199 static struct option longopts[] = {
200 { "version", no_argument0, NULL((void *)0), 'V' },
201 { NULL((void *)0), 0, NULL((void *)0), 0 }
202 };
203
204 setlocale(LC_CTYPE2, "");
205
206 while ((ch = getopt_long(argc, argv, "+hV", longopts, NULL((void *)0))) != -1) {
207 switch (ch) {
208 case 'h':
209 hflag = 1;
210 break;
211 case 'V':
212 Vflag = 1;
213 break;
214 default:
215 usage(hflag, 1);
216 /* NOTREACHED */
217 }
218 }
219
220 argc -= optind;
221 argv += optind;
222 optind = 1;
223 optreset = 1;
224
225 if (Vflag) {
226 got_version_print_str();
227 return 0;
228 }
229
230 if (argc <= 0)
231 usage(hflag, hflag ? 0 : 1);
232
233 signal(SIGINT2, catch_sigint);
234 signal(SIGPIPE13, catch_sigpipe);
235
236 for (i = 0; i < nitems(got_commands)(sizeof((got_commands)) / sizeof((got_commands)[0])); i++) {
237 const struct got_error *error;
238
239 cmd = &got_commands[i];
240
241 if (strcmp(cmd->cmd_name, argv[0]) != 0 &&
242 strcmp(cmd->cmd_alias, argv[0]) != 0)
243 continue;
244
245 if (hflag)
246 got_commands[i].cmd_usage();
247
248 error = got_commands[i].cmd_main(argc, argv);
249 if (error && error->code != GOT_ERR_CANCELLED49 &&
250 error->code != GOT_ERR_PRIVSEP_EXIT41 &&
251 !(sigpipe_received &&
252 error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EPIPE32) &&
253 !(sigint_received &&
254 error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EINTR4)) {
255 fprintf(stderr(&__sF[2]), "%s: %s\n", getprogname(), error->msg);
256 return 1;
257 }
258
259 return 0;
260 }
261
262 fprintf(stderr(&__sF[2]), "%s: unknown command '%s'\n", getprogname(), argv[0]);
263 list_commands(stderr(&__sF[2]));
264 return 1;
265}
266
267__dead__attribute__((__noreturn__)) static void
268usage(int hflag, int status)
269{
270 FILE *fp = (status == 0) ? stdout(&__sF[1]) : stderr(&__sF[2]);
271
272 fprintf(fp, "usage: %s [-h] [-V | --version] command [arg ...]\n",
273 getprogname());
274 if (hflag)
275 list_commands(fp);
276 exit(status);
277}
278
279static const struct got_error *
280get_editor(char **abspath)
281{
282 const struct got_error *err = NULL((void *)0);
283 const char *editor;
284
285 *abspath = NULL((void *)0);
286
287 editor = getenv("VISUAL");
288 if (editor == NULL((void *)0))
289 editor = getenv("EDITOR");
290
291 if (editor) {
292 err = got_path_find_prog(abspath, editor);
293 if (err)
294 return err;
295 }
296
297 if (*abspath == NULL((void *)0)) {
298 *abspath = strdup("/bin/ed");
299 if (*abspath == NULL((void *)0))
300 return got_error_from_errno("strdup");
301 }
302
303 return NULL((void *)0);
304}
305
306static const struct got_error *
307apply_unveil(const char *repo_path, int repo_read_only,
308 const char *worktree_path)
309{
310 const struct got_error *err;
311
312#ifdef PROFILE
313 if (unveil("gmon.out", "rwc") != 0)
314 return got_error_from_errno2("unveil", "gmon.out");
315#endif
316 if (repo_path && unveil(repo_path, repo_read_only ? "r" : "rwc") != 0)
317 return got_error_from_errno2("unveil", repo_path);
318
319 if (worktree_path && unveil(worktree_path, "rwc") != 0)
320 return got_error_from_errno2("unveil", worktree_path);
321
322 if (unveil(GOT_TMPDIR_STR"/tmp", "rwc") != 0)
323 return got_error_from_errno2("unveil", GOT_TMPDIR_STR"/tmp");
324
325 err = got_privsep_unveil_exec_helpers();
326 if (err != NULL((void *)0))
327 return err;
328
329 if (unveil(NULL((void *)0), NULL((void *)0)) != 0)
330 return got_error_from_errno("unveil");
331
332 return NULL((void *)0);
333}
334
335__dead__attribute__((__noreturn__)) static void
336usage_init(void)
337{
338 fprintf(stderr(&__sF[2]), "usage: %s init repository-path\n", getprogname());
339 exit(1);
340}
341
342static const struct got_error *
343cmd_init(int argc, char *argv[])
344{
345 const struct got_error *error = NULL((void *)0);
346 char *repo_path = NULL((void *)0);
347 int ch;
348
349 while ((ch = getopt(argc, argv, "")) != -1) {
350 switch (ch) {
351 default:
352 usage_init();
353 /* NOTREACHED */
354 }
355 }
356
357 argc -= optind;
358 argv += optind;
359
360#ifndef PROFILE
361 if (pledge("stdio rpath wpath cpath unveil", NULL((void *)0)) == -1)
362 err(1, "pledge");
363#endif
364 if (argc != 1)
365 usage_init();
366
367 repo_path = strdup(argv[0]);
368 if (repo_path == NULL((void *)0))
369 return got_error_from_errno("strdup");
370
371 got_path_strip_trailing_slashes(repo_path);
372
373 error = got_path_mkdir(repo_path);
374 if (error &&
375 !(error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EEXIST17))
376 goto done;
377
378 error = apply_unveil(repo_path, 0, NULL((void *)0));
379 if (error)
380 goto done;
381
382 error = got_repo_init(repo_path);
383done:
384 free(repo_path);
385 return error;
386}
387
388__dead__attribute__((__noreturn__)) static void
389usage_import(void)
390{
391 fprintf(stderr(&__sF[2]), "usage: %s import [-b branch] [-m message] "
392 "[-r repository-path] [-I pattern] path\n", getprogname());
393 exit(1);
394}
395
396int
397spawn_editor(const char *editor, const char *file)
398{
399 pid_t pid;
400 sig_t sighup, sigint, sigquit;
401 int st = -1;
402
403 sighup = signal(SIGHUP1, SIG_IGN(void (*)(int))1);
404 sigint = signal(SIGINT2, SIG_IGN(void (*)(int))1);
405 sigquit = signal(SIGQUIT3, SIG_IGN(void (*)(int))1);
406
407 switch (pid = fork()) {
408 case -1:
409 goto doneediting;
410 case 0:
411 execl(editor, editor, file, (char *)NULL((void *)0));
412 _exit(127);
413 }
414
415 while (waitpid(pid, &st, 0) == -1)
416 if (errno(*__errno()) != EINTR4)
417 break;
418
419doneediting:
420 (void)signal(SIGHUP1, sighup);
421 (void)signal(SIGINT2, sigint);
422 (void)signal(SIGQUIT3, sigquit);
423
424 if (!WIFEXITED(st)(((st) & 0177) == 0)) {
425 errno(*__errno()) = EINTR4;
426 return -1;
427 }
428
429 return WEXITSTATUS(st)(int)(((unsigned)(st) >> 8) & 0xff);
430}
431
432static const struct got_error *
433edit_logmsg(char **logmsg, const char *editor, const char *logmsg_path,
434 const char *initial_content, size_t initial_content_len,
435 int require_modification)
436{
437 const struct got_error *err = NULL((void *)0);
438 char *line = NULL((void *)0);
439 size_t linesize = 0;
440 ssize_t linelen;
441 struct stat st, st2;
442 FILE *fp = NULL((void *)0);
443 size_t len, logmsg_len;
444 char *initial_content_stripped = NULL((void *)0), *buf = NULL((void *)0), *s;
445
446 *logmsg = NULL((void *)0);
447
448 if (stat(logmsg_path, &st) == -1)
449 return got_error_from_errno2("stat", logmsg_path);
450
451 if (spawn_editor(editor, logmsg_path) == -1)
452 return got_error_from_errno("failed spawning editor");
453
454 if (stat(logmsg_path, &st2) == -1)
455 return got_error_from_errno("stat");
456
457 if (require_modification &&
458 st.st_mtimest_mtim.tv_sec == st2.st_mtimest_mtim.tv_sec && st.st_size == st2.st_size)
459 return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY74,
460 "no changes made to commit message, aborting");
461
462 /*
463 * Set up a stripped version of the initial content without comments
464 * and blank lines. We need this in order to check if the message
465 * has in fact been edited.
466 */
467 initial_content_stripped = malloc(initial_content_len + 1);
468 if (initial_content_stripped == NULL((void *)0))
469 return got_error_from_errno("malloc");
470 initial_content_stripped[0] = '\0';
471
472 buf = strdup(initial_content);
473 if (buf == NULL((void *)0)) {
474 err = got_error_from_errno("strdup");
475 goto done;
476 }
477 s = buf;
478 len = 0;
479 while ((line = strsep(&s, "\n")) != NULL((void *)0)) {
480 if ((line[0] == '#' || (len == 0 && line[0] == '\n')))
481 continue; /* remove comments and leading empty lines */
482 len = strlcat(initial_content_stripped, line,
483 initial_content_len + 1);
484 if (len >= initial_content_len + 1) {
485 err = got_error(GOT_ERR_NO_SPACE9);
486 goto done;
487 }
488 }
489 while (len > 0 && initial_content_stripped[len - 1] == '\n') {
490 initial_content_stripped[len - 1] = '\0';
491 len--;
492 }
493
494 logmsg_len = st2.st_size;
495 *logmsg = malloc(logmsg_len + 1);
496 if (*logmsg == NULL((void *)0))
497 return got_error_from_errno("malloc");
498 (*logmsg)[0] = '\0';
499
500 fp = fopen(logmsg_path, "r");
501 if (fp == NULL((void *)0)) {
502 err = got_error_from_errno("fopen");
503 goto done;
504 }
505
506 len = 0;
507 while ((linelen = getline(&line, &linesize, fp)) != -1) {
508 if ((line[0] == '#' || (len == 0 && line[0] == '\n')))
509 continue; /* remove comments and leading empty lines */
510 len = strlcat(*logmsg, line, logmsg_len + 1);
511 if (len >= logmsg_len + 1) {
512 err = got_error(GOT_ERR_NO_SPACE9);
513 goto done;
514 }
515 }
516 free(line);
517 if (ferror(fp)(!__isthreaded ? (((fp)->_flags & 0x0040) != 0) : (ferror
)(fp))
) {
518 err = got_ferror(fp, GOT_ERR_IO6);
519 goto done;
520 }
521 while (len > 0 && (*logmsg)[len - 1] == '\n') {
522 (*logmsg)[len - 1] = '\0';
523 len--;
524 }
525
526 if (len == 0) {
527 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY74,
528 "commit message cannot be empty, aborting");
529 goto done;
530 }
531 if (require_modification &&
532 strcmp(*logmsg, initial_content_stripped) == 0)
533 err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY74,
534 "no changes made to commit message, aborting");
535done:
536 free(initial_content_stripped);
537 free(buf);
538 if (fp && fclose(fp) == EOF(-1) && err == NULL((void *)0))
539 err = got_error_from_errno("fclose");
540 if (err) {
541 free(*logmsg);
542 *logmsg = NULL((void *)0);
543 }
544 return err;
545}
546
547static const struct got_error *
548collect_import_msg(char **logmsg, char **logmsg_path, const char *editor,
549 const char *path_dir, const char *branch_name)
550{
551 char *initial_content = NULL((void *)0);
552 const struct got_error *err = NULL((void *)0);
553 int initial_content_len;
554 int fd = -1;
555
556 initial_content_len = asprintf(&initial_content,
557 "\n# %s to be imported to branch %s\n", path_dir,
558 branch_name);
559 if (initial_content_len == -1)
560 return got_error_from_errno("asprintf");
561
562 err = got_opentemp_named_fd(logmsg_path, &fd,
563 GOT_TMPDIR_STR"/tmp" "/got-importmsg");
564 if (err)
565 goto done;
566
567 if (write(fd, initial_content, initial_content_len) == -1) {
568 err = got_error_from_errno2("write", *logmsg_path);
569 goto done;
570 }
571
572 err = edit_logmsg(logmsg, editor, *logmsg_path, initial_content,
573 initial_content_len, 1);
574done:
575 if (fd != -1 && close(fd) == -1 && err == NULL((void *)0))
576 err = got_error_from_errno2("close", *logmsg_path);
577 free(initial_content);
578 if (err) {
579 free(*logmsg_path);
580 *logmsg_path = NULL((void *)0);
581 }
582 return err;
583}
584
585static const struct got_error *
586import_progress(void *arg, const char *path)
587{
588 printf("A %s\n", path);
589 return NULL((void *)0);
590}
591
592static const struct got_error *
593get_author(char **author, struct got_repository *repo,
594 struct got_worktree *worktree)
595{
596 const struct got_error *err = NULL((void *)0);
597 const char *got_author = NULL((void *)0), *name, *email;
598 const struct got_gotconfig *worktree_conf = NULL((void *)0), *repo_conf = NULL((void *)0);
599
600 *author = NULL((void *)0);
601
602 if (worktree)
603 worktree_conf = got_worktree_get_gotconfig(worktree);
604 repo_conf = got_repo_get_gotconfig(repo);
605
606 /*
607 * Priority of potential author information sources, from most
608 * significant to least significant:
609 * 1) work tree's .got/got.conf file
610 * 2) repository's got.conf file
611 * 3) repository's git config file
612 * 4) environment variables
613 * 5) global git config files (in user's home directory or /etc)
614 */
615
616 if (worktree_conf)
617 got_author = got_gotconfig_get_author(worktree_conf);
618 if (got_author == NULL((void *)0))
619 got_author = got_gotconfig_get_author(repo_conf);
620 if (got_author == NULL((void *)0)) {
621 name = got_repo_get_gitconfig_author_name(repo);
622 email = got_repo_get_gitconfig_author_email(repo);
623 if (name && email) {
624 if (asprintf(author, "%s <%s>", name, email) == -1)
625 return got_error_from_errno("asprintf");
626 return NULL((void *)0);
627 }
628
629 got_author = getenv("GOT_AUTHOR");
630 if (got_author == NULL((void *)0)) {
631 name = got_repo_get_global_gitconfig_author_name(repo);
632 email = got_repo_get_global_gitconfig_author_email(
633 repo);
634 if (name && email) {
635 if (asprintf(author, "%s <%s>", name, email)
636 == -1)
637 return got_error_from_errno("asprintf");
638 return NULL((void *)0);
639 }
640 /* TODO: Look up user in password database? */
641 return got_error(GOT_ERR_COMMIT_NO_AUTHOR71);
642 }
643 }
644
645 *author = strdup(got_author);
646 if (*author == NULL((void *)0))
647 return got_error_from_errno("strdup");
648
649 /*
650 * Really dumb email address check; we're only doing this to
651 * avoid git's object parser breaking on commits we create.
652 */
653 while (*got_author && *got_author != '<')
654 got_author++;
655 if (*got_author != '<') {
656 err = got_error(GOT_ERR_COMMIT_NO_EMAIL108);
657 goto done;
658 }
659 while (*got_author && *got_author != '@')
660 got_author++;
661 if (*got_author != '@') {
662 err = got_error(GOT_ERR_COMMIT_NO_EMAIL108);
663 goto done;
664 }
665 while (*got_author && *got_author != '>')
666 got_author++;
667 if (*got_author != '>')
668 err = got_error(GOT_ERR_COMMIT_NO_EMAIL108);
669done:
670 if (err) {
671 free(*author);
672 *author = NULL((void *)0);
673 }
674 return err;
675}
676
677static const struct got_error *
678get_gitconfig_path(char **gitconfig_path)
679{
680 const char *homedir = getenv("HOME");
681
682 *gitconfig_path = NULL((void *)0);
683 if (homedir) {
684 if (asprintf(gitconfig_path, "%s/.gitconfig", homedir) == -1)
685 return got_error_from_errno("asprintf");
686
687 }
688 return NULL((void *)0);
689}
690
691static const struct got_error *
692cmd_import(int argc, char *argv[])
693{
694 const struct got_error *error = NULL((void *)0);
695 char *path_dir = NULL((void *)0), *repo_path = NULL((void *)0), *logmsg = NULL((void *)0);
696 char *gitconfig_path = NULL((void *)0), *editor = NULL((void *)0), *author = NULL((void *)0);
697 const char *branch_name = "main";
698 char *refname = NULL((void *)0), *id_str = NULL((void *)0), *logmsg_path = NULL((void *)0);
699 struct got_repository *repo = NULL((void *)0);
700 struct got_reference *branch_ref = NULL((void *)0), *head_ref = NULL((void *)0);
701 struct got_object_id *new_commit_id = NULL((void *)0);
702 int ch;
703 struct got_pathlist_head ignores;
704 struct got_pathlist_entry *pe;
705 int preserve_logmsg = 0;
706
707 TAILQ_INIT(&ignores)do { (&ignores)->tqh_first = ((void *)0); (&ignores
)->tqh_last = &(&ignores)->tqh_first; } while (
0)
;
708
709 while ((ch = getopt(argc, argv, "b:m:r:I:")) != -1) {
710 switch (ch) {
711 case 'b':
712 branch_name = optarg;
713 break;
714 case 'm':
715 logmsg = strdup(optarg);
716 if (logmsg == NULL((void *)0)) {
717 error = got_error_from_errno("strdup");
718 goto done;
719 }
720 break;
721 case 'r':
722 repo_path = realpath(optarg, NULL((void *)0));
723 if (repo_path == NULL((void *)0)) {
724 error = got_error_from_errno2("realpath",
725 optarg);
726 goto done;
727 }
728 break;
729 case 'I':
730 if (optarg[0] == '\0')
731 break;
732 error = got_pathlist_insert(&pe, &ignores, optarg,
733 NULL((void *)0));
734 if (error)
735 goto done;
736 break;
737 default:
738 usage_import();
739 /* NOTREACHED */
740 }
741 }
742
743 argc -= optind;
744 argv += optind;
745
746#ifndef PROFILE
747 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
748 "unveil",
749 NULL((void *)0)) == -1)
750 err(1, "pledge");
751#endif
752 if (argc != 1)
753 usage_import();
754
755 if (repo_path == NULL((void *)0)) {
756 repo_path = getcwd(NULL((void *)0), 0);
757 if (repo_path == NULL((void *)0))
758 return got_error_from_errno("getcwd");
759 }
760 got_path_strip_trailing_slashes(repo_path);
761 error = get_gitconfig_path(&gitconfig_path);
762 if (error)
763 goto done;
764 error = got_repo_open(&repo, repo_path, gitconfig_path);
765 if (error)
766 goto done;
767
768 error = get_author(&author, repo, NULL((void *)0));
769 if (error)
770 return error;
771
772 /*
773 * Don't let the user create a branch name with a leading '-'.
774 * While technically a valid reference name, this case is usually
775 * an unintended typo.
776 */
777 if (branch_name[0] == '-')
778 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS113);
779
780 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
781 error = got_error_from_errno("asprintf");
782 goto done;
783 }
784
785 error = got_ref_open(&branch_ref, repo, refname, 0);
786 if (error) {
787 if (error->code != GOT_ERR_NOT_REF5)
788 goto done;
789 } else {
790 error = got_error_msg(GOT_ERR_BRANCH_EXISTS83,
791 "import target branch already exists");
792 goto done;
793 }
794
795 path_dir = realpath(argv[0], NULL((void *)0));
796 if (path_dir == NULL((void *)0)) {
797 error = got_error_from_errno2("realpath", argv[0]);
798 goto done;
799 }
800 got_path_strip_trailing_slashes(path_dir);
801
802 /*
803 * unveil(2) traverses exec(2); if an editor is used we have
804 * to apply unveil after the log message has been written.
805 */
806 if (logmsg == NULL((void *)0) || strlen(logmsg) == 0) {
807 error = get_editor(&editor);
808 if (error)
809 goto done;
810 free(logmsg);
811 error = collect_import_msg(&logmsg, &logmsg_path, editor,
812 path_dir, refname);
813 if (error) {
814 if (error->code != GOT_ERR_COMMIT_MSG_EMPTY74 &&
815 logmsg_path != NULL((void *)0))
816 preserve_logmsg = 1;
817 goto done;
818 }
819 }
820
821 if (unveil(path_dir, "r") != 0) {
822 error = got_error_from_errno2("unveil", path_dir);
823 if (logmsg_path)
824 preserve_logmsg = 1;
825 goto done;
826 }
827
828 error = apply_unveil(got_repo_get_path(repo), 0, NULL((void *)0));
829 if (error) {
830 if (logmsg_path)
831 preserve_logmsg = 1;
832 goto done;
833 }
834
835 error = got_repo_import(&new_commit_id, path_dir, logmsg,
836 author, &ignores, repo, import_progress, NULL((void *)0));
837 if (error) {
838 if (logmsg_path)
839 preserve_logmsg = 1;
840 goto done;
841 }
842
843 error = got_ref_alloc(&branch_ref, refname, new_commit_id);
844 if (error) {
845 if (logmsg_path)
846 preserve_logmsg = 1;
847 goto done;
848 }
849
850 error = got_ref_write(branch_ref, repo);
851 if (error) {
852 if (logmsg_path)
853 preserve_logmsg = 1;
854 goto done;
855 }
856
857 error = got_object_id_str(&id_str, new_commit_id);
858 if (error) {
859 if (logmsg_path)
860 preserve_logmsg = 1;
861 goto done;
862 }
863
864 error = got_ref_open(&head_ref, repo, GOT_REF_HEAD"HEAD", 0);
865 if (error) {
866 if (error->code != GOT_ERR_NOT_REF5) {
867 if (logmsg_path)
868 preserve_logmsg = 1;
869 goto done;
870 }
871
872 error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD"HEAD",
873 branch_ref);
874 if (error) {
875 if (logmsg_path)
876 preserve_logmsg = 1;
877 goto done;
878 }
879
880 error = got_ref_write(head_ref, repo);
881 if (error) {
882 if (logmsg_path)
883 preserve_logmsg = 1;
884 goto done;
885 }
886 }
887
888 printf("Created branch %s with commit %s\n",
889 got_ref_get_name(branch_ref), id_str);
890done:
891 if (preserve_logmsg) {
892 fprintf(stderr(&__sF[2]), "%s: log message preserved in %s\n",
893 getprogname(), logmsg_path);
894 } else if (logmsg_path && unlink(logmsg_path) == -1 && error == NULL((void *)0))
895 error = got_error_from_errno2("unlink", logmsg_path);
896 free(logmsg);
897 free(logmsg_path);
898 free(repo_path);
899 free(editor);
900 free(refname);
901 free(new_commit_id);
902 free(id_str);
903 free(author);
904 free(gitconfig_path);
905 if (branch_ref)
906 got_ref_close(branch_ref);
907 if (head_ref)
908 got_ref_close(head_ref);
909 return error;
910}
911
912__dead__attribute__((__noreturn__)) static void
913usage_clone(void)
914{
915 fprintf(stderr(&__sF[2]), "usage: %s clone [-a] [-b branch] [-l] [-m] [-q] [-v] "
916 "[-R reference] repository-url [directory]\n", getprogname());
917 exit(1);
918}
919
920struct got_fetch_progress_arg {
921 char last_scaled_size[FMT_SCALED_STRSIZE7];
922 int last_p_indexed;
923 int last_p_resolved;
924 int verbosity;
925
926 struct got_repository *repo;
927
928 int create_configs;
929 int configs_created;
930 struct {
931 struct got_pathlist_head *symrefs;
932 struct got_pathlist_head *wanted_branches;
933 struct got_pathlist_head *wanted_refs;
934 const char *proto;
935 const char *host;
936 const char *port;
937 const char *remote_repo_path;
938 const char *git_url;
939 int fetch_all_branches;
940 int mirror_references;
941 } config_info;
942};
943
944/* XXX forward declaration */
945static const struct got_error *
946create_config_files(const char *proto, const char *host, const char *port,
947 const char *remote_repo_path, const char *git_url, int fetch_all_branches,
948 int mirror_references, struct got_pathlist_head *symrefs,
949 struct got_pathlist_head *wanted_branches,
950 struct got_pathlist_head *wanted_refs, struct got_repository *repo);
951
952static const struct got_error *
953fetch_progress(void *arg, const char *message, off_t packfile_size,
954 int nobj_total, int nobj_indexed, int nobj_loose, int nobj_resolved)
955{
956 const struct got_error *err = NULL((void *)0);
957 struct got_fetch_progress_arg *a = arg;
958 char scaled_size[FMT_SCALED_STRSIZE7];
959 int p_indexed, p_resolved;
960 int print_size = 0, print_indexed = 0, print_resolved = 0;
961
962 /*
963 * In order to allow a failed clone to be resumed with 'got fetch'
964 * we try to create configuration files as soon as possible.
965 * Once the server has sent information about its default branch
966 * we have all required information.
967 */
968 if (a->create_configs && !a->configs_created &&
969 !TAILQ_EMPTY(a->config_info.symrefs)(((a->config_info.symrefs)->tqh_first) == ((void *)0))) {
970 err = create_config_files(a->config_info.proto,
971 a->config_info.host, a->config_info.port,
972 a->config_info.remote_repo_path,
973 a->config_info.git_url,
974 a->config_info.fetch_all_branches,
975 a->config_info.mirror_references,
976 a->config_info.symrefs,
977 a->config_info.wanted_branches,
978 a->config_info.wanted_refs, a->repo);
979 if (err)
980 return err;
981 a->configs_created = 1;
982 }
983
984 if (a->verbosity < 0)
985 return NULL((void *)0);
986
987 if (message && message[0] != '\0') {
988 printf("\rserver: %s", message);
989 fflush(stdout(&__sF[1]));
990 return NULL((void *)0);
991 }
992
993 if (packfile_size > 0 || nobj_indexed > 0) {
994 if (fmt_scaled(packfile_size, scaled_size) == 0 &&
995 (a->last_scaled_size[0] == '\0' ||
996 strcmp(scaled_size, a->last_scaled_size)) != 0) {
997 print_size = 1;
998 if (strlcpy(a->last_scaled_size, scaled_size,
999 FMT_SCALED_STRSIZE7) >= FMT_SCALED_STRSIZE7)
1000 return got_error(GOT_ERR_NO_SPACE9);
1001 }
1002 if (nobj_indexed > 0) {
1003 p_indexed = (nobj_indexed * 100) / nobj_total;
1004 if (p_indexed != a->last_p_indexed) {
1005 a->last_p_indexed = p_indexed;
1006 print_indexed = 1;
1007 print_size = 1;
1008 }
1009 }
1010 if (nobj_resolved > 0) {
1011 p_resolved = (nobj_resolved * 100) /
1012 (nobj_total - nobj_loose);
1013 if (p_resolved != a->last_p_resolved) {
1014 a->last_p_resolved = p_resolved;
1015 print_resolved = 1;
1016 print_indexed = 1;
1017 print_size = 1;
1018 }
1019 }
1020
1021 }
1022 if (print_size || print_indexed || print_resolved)
1023 printf("\r");
1024 if (print_size)
1025 printf("%*s fetched", FMT_SCALED_STRSIZE7, scaled_size);
1026 if (print_indexed)
1027 printf("; indexing %d%%", p_indexed);
1028 if (print_resolved)
1029 printf("; resolving deltas %d%%", p_resolved);
1030 if (print_size || print_indexed || print_resolved)
1031 fflush(stdout(&__sF[1]));
1032
1033 return NULL((void *)0);
1034}
1035
1036static const struct got_error *
1037create_symref(const char *refname, struct got_reference *target_ref,
1038 int verbosity, struct got_repository *repo)
1039{
1040 const struct got_error *err;
1041 struct got_reference *head_symref;
1042
1043 err = got_ref_alloc_symref(&head_symref, refname, target_ref);
1044 if (err)
1045 return err;
1046
1047 err = got_ref_write(head_symref, repo);
1048 if (err == NULL((void *)0) && verbosity > 0) {
1049 printf("Created reference %s: %s\n", GOT_REF_HEAD"HEAD",
1050 got_ref_get_name(target_ref));
1051 }
1052 got_ref_close(head_symref);
1053 return err;
1054}
1055
1056static const struct got_error *
1057list_remote_refs(struct got_pathlist_head *symrefs,
1058 struct got_pathlist_head *refs)
1059{
1060 const struct got_error *err;
1061 struct got_pathlist_entry *pe;
1062
1063 TAILQ_FOREACH(pe, symrefs, entry)for((pe) = ((symrefs)->tqh_first); (pe) != ((void *)0); (pe
) = ((pe)->entry.tqe_next))
{
1064 const char *refname = pe->path;
1065 const char *targetref = pe->data;
1066
1067 printf("%s: %s\n", refname, targetref);
1068 }
1069
1070 TAILQ_FOREACH(pe, refs, entry)for((pe) = ((refs)->tqh_first); (pe) != ((void *)0); (pe) =
((pe)->entry.tqe_next))
{
1071 const char *refname = pe->path;
1072 struct got_object_id *id = pe->data;
1073 char *id_str;
1074
1075 err = got_object_id_str(&id_str, id);
1076 if (err)
1077 return err;
1078 printf("%s: %s\n", refname, id_str);
1079 free(id_str);
1080 }
1081
1082 return NULL((void *)0);
1083}
1084
1085static const struct got_error *
1086create_ref(const char *refname, struct got_object_id *id,
1087 int verbosity, struct got_repository *repo)
1088{
1089 const struct got_error *err = NULL((void *)0);
1090 struct got_reference *ref;
1091 char *id_str;
1092
1093 err = got_object_id_str(&id_str, id);
1094 if (err)
1095 return err;
1096
1097 err = got_ref_alloc(&ref, refname, id);
1098 if (err)
1099 goto done;
1100
1101 err = got_ref_write(ref, repo);
1102 got_ref_close(ref);
1103
1104 if (err == NULL((void *)0) && verbosity >= 0)
1105 printf("Created reference %s: %s\n", refname, id_str);
1106done:
1107 free(id_str);
1108 return err;
1109}
1110
1111static int
1112match_wanted_ref(const char *refname, const char *wanted_ref)
1113{
1114 if (strncmp(refname, "refs/", 5) != 0)
1115 return 0;
1116 refname += 5;
1117
1118 /*
1119 * Prevent fetching of references that won't make any
1120 * sense outside of the remote repository's context.
1121 */
1122 if (strncmp(refname, "got/", 4) == 0)
1123 return 0;
1124 if (strncmp(refname, "remotes/", 8) == 0)
1125 return 0;
1126
1127 if (strncmp(wanted_ref, "refs/", 5) == 0)
1128 wanted_ref += 5;
1129
1130 /* Allow prefix match. */
1131 if (got_path_is_child(refname, wanted_ref, strlen(wanted_ref)))
1132 return 1;
1133
1134 /* Allow exact match. */
1135 return (strcmp(refname, wanted_ref) == 0);
1136}
1137
1138static int
1139is_wanted_ref(struct got_pathlist_head *wanted_refs, const char *refname)
1140{
1141 struct got_pathlist_entry *pe;
1142
1143 TAILQ_FOREACH(pe, wanted_refs, entry)for((pe) = ((wanted_refs)->tqh_first); (pe) != ((void *)0)
; (pe) = ((pe)->entry.tqe_next))
{
1144 if (match_wanted_ref(refname, pe->path))
1145 return 1;
1146 }
1147
1148 return 0;
1149}
1150
1151static const struct got_error *
1152create_wanted_ref(const char *refname, struct got_object_id *id,
1153 const char *remote_repo_name, int verbosity, struct got_repository *repo)
1154{
1155 const struct got_error *err;
1156 char *remote_refname;
1157
1158 if (strncmp("refs/", refname, 5) == 0)
1159 refname += 5;
1160
1161 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
1162 remote_repo_name, refname) == -1)
1163 return got_error_from_errno("asprintf");
1164
1165 err = create_ref(remote_refname, id, verbosity, repo);
1166 free(remote_refname);
1167 return err;
1168}
1169
1170static const struct got_error *
1171create_gotconfig(const char *proto, const char *host, const char *port,
1172 const char *remote_repo_path, const char *default_branch,
1173 int fetch_all_branches, struct got_pathlist_head *wanted_branches,
1174 struct got_pathlist_head *wanted_refs, int mirror_references,
1175 struct got_repository *repo)
1176{
1177 const struct got_error *err = NULL((void *)0);
1178 char *gotconfig_path = NULL((void *)0);
1179 char *gotconfig = NULL((void *)0);
1180 FILE *gotconfig_file = NULL((void *)0);
1181 const char *branchname = NULL((void *)0);
1182 char *branches = NULL((void *)0), *refs = NULL((void *)0);
1183 ssize_t n;
1184
1185 if (!fetch_all_branches && !TAILQ_EMPTY(wanted_branches)(((wanted_branches)->tqh_first) == ((void *)0))) {
1186 struct got_pathlist_entry *pe;
1187 TAILQ_FOREACH(pe, wanted_branches, entry)for((pe) = ((wanted_branches)->tqh_first); (pe) != ((void *
)0); (pe) = ((pe)->entry.tqe_next))
{
1188 char *s;
1189 branchname = pe->path;
1190 if (strncmp(branchname, "refs/heads/", 11) == 0)
1191 branchname += 11;
1192 if (asprintf(&s, "%s\"%s\" ",
1193 branches ? branches : "", branchname) == -1) {
1194 err = got_error_from_errno("asprintf");
1195 goto done;
1196 }
1197 free(branches);
1198 branches = s;
1199 }
1200 } else if (!fetch_all_branches && default_branch) {
1201 branchname = default_branch;
1202 if (strncmp(branchname, "refs/heads/", 11) == 0)
1203 branchname += 11;
1204 if (asprintf(&branches, "\"%s\" ", branchname) == -1) {
1205 err = got_error_from_errno("asprintf");
1206 goto done;
1207 }
1208 }
1209 if (!TAILQ_EMPTY(wanted_refs)(((wanted_refs)->tqh_first) == ((void *)0))) {
1210 struct got_pathlist_entry *pe;
1211 TAILQ_FOREACH(pe, wanted_refs, entry)for((pe) = ((wanted_refs)->tqh_first); (pe) != ((void *)0)
; (pe) = ((pe)->entry.tqe_next))
{
1212 char *s;
1213 const char *refname = pe->path;
1214 if (strncmp(refname, "refs/", 5) == 0)
1215 branchname += 5;
1216 if (asprintf(&s, "%s\"%s\" ",
1217 refs ? refs : "", refname) == -1) {
1218 err = got_error_from_errno("asprintf");
1219 goto done;
1220 }
1221 free(refs);
1222 refs = s;
1223 }
1224 }
1225
1226 /* Create got.conf(5). */
1227 gotconfig_path = got_repo_get_path_gotconfig(repo);
1228 if (gotconfig_path == NULL((void *)0)) {
1229 err = got_error_from_errno("got_repo_get_path_gotconfig");
1230 goto done;
1231 }
1232 gotconfig_file = fopen(gotconfig_path, "a");
1233 if (gotconfig_file == NULL((void *)0)) {
1234 err = got_error_from_errno2("fopen", gotconfig_path);
1235 goto done;
1236 }
1237 if (asprintf(&gotconfig,
1238 "remote \"%s\" {\n"
1239 "\tserver %s\n"
1240 "\tprotocol %s\n"
1241 "%s%s%s"
1242 "\trepository \"%s\"\n"
1243 "%s%s%s"
1244 "%s%s%s"
1245 "%s"
1246 "%s"
1247 "}\n",
1248 GOT_FETCH_DEFAULT_REMOTE_NAME"origin", host, proto,
1249 port ? "\tport " : "", port ? port : "", port ? "\n" : "",
1250 remote_repo_path, branches ? "\tbranch { " : "",
1251 branches ? branches : "", branches ? "}\n" : "",
1252 refs ? "\treference { " : "", refs ? refs : "", refs ? "}\n" : "",
1253 mirror_references ? "\tmirror-references yes\n" : "",
1254 fetch_all_branches ? "\tfetch-all-branches yes\n" : "") == -1) {
1255 err = got_error_from_errno("asprintf");
1256 goto done;
1257 }
1258 n = fwrite(gotconfig, 1, strlen(gotconfig), gotconfig_file);
1259 if (n != strlen(gotconfig)) {
1260 err = got_ferror(gotconfig_file, GOT_ERR_IO6);
1261 goto done;
1262 }
1263
1264done:
1265 if (gotconfig_file && fclose(gotconfig_file) == EOF(-1) && err == NULL((void *)0))
1266 err = got_error_from_errno2("fclose", gotconfig_path);
1267 free(gotconfig_path);
1268 free(branches);
1269 return err;
1270}
1271
1272static const struct got_error *
1273create_gitconfig(const char *git_url, const char *default_branch,
1274 int fetch_all_branches, struct got_pathlist_head *wanted_branches,
1275 struct got_pathlist_head *wanted_refs, int mirror_references,
1276 struct got_repository *repo)
1277{
1278 const struct got_error *err = NULL((void *)0);
1279 char *gitconfig_path = NULL((void *)0);
1280 char *gitconfig = NULL((void *)0);
1281 FILE *gitconfig_file = NULL((void *)0);
1282 char *branches = NULL((void *)0), *refs = NULL((void *)0);
1283 const char *branchname;
1284 ssize_t n;
1285
1286 /* Create a config file Git can understand. */
1287 gitconfig_path = got_repo_get_path_gitconfig(repo);
1288 if (gitconfig_path == NULL((void *)0)) {
1289 err = got_error_from_errno("got_repo_get_path_gitconfig");
1290 goto done;
1291 }
1292 gitconfig_file = fopen(gitconfig_path, "a");
1293 if (gitconfig_file == NULL((void *)0)) {
1294 err = got_error_from_errno2("fopen", gitconfig_path);
1295 goto done;
1296 }
1297 if (fetch_all_branches) {
1298 if (mirror_references) {
1299 if (asprintf(&branches,
1300 "\tfetch = refs/heads/*:refs/heads/*\n") == -1) {
1301 err = got_error_from_errno("asprintf");
1302 goto done;
1303 }
1304 } else if (asprintf(&branches,
1305 "\tfetch = refs/heads/*:refs/remotes/%s/*\n",
1306 GOT_FETCH_DEFAULT_REMOTE_NAME"origin") == -1) {
1307 err = got_error_from_errno("asprintf");
1308 goto done;
1309 }
1310 } else if (!TAILQ_EMPTY(wanted_branches)(((wanted_branches)->tqh_first) == ((void *)0))) {
1311 struct got_pathlist_entry *pe;
1312 TAILQ_FOREACH(pe, wanted_branches, entry)for((pe) = ((wanted_branches)->tqh_first); (pe) != ((void *
)0); (pe) = ((pe)->entry.tqe_next))
{
1313 char *s;
1314 branchname = pe->path;
1315 if (strncmp(branchname, "refs/heads/", 11) == 0)
1316 branchname += 11;
1317 if (mirror_references) {
1318 if (asprintf(&s,
1319 "%s\tfetch = refs/heads/%s:refs/heads/%s\n",
1320 branches ? branches : "",
1321 branchname, branchname) == -1) {
1322 err = got_error_from_errno("asprintf");
1323 goto done;
1324 }
1325 } else if (asprintf(&s,
1326 "%s\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1327 branches ? branches : "",
1328 branchname, GOT_FETCH_DEFAULT_REMOTE_NAME"origin",
1329 branchname) == -1) {
1330 err = got_error_from_errno("asprintf");
1331 goto done;
1332 }
1333 free(branches);
1334 branches = s;
1335 }
1336 } else {
1337 /*
1338 * If the server specified a default branch, use just that one.
1339 * Otherwise fall back to fetching all branches on next fetch.
1340 */
1341 if (default_branch) {
1342 branchname = default_branch;
1343 if (strncmp(branchname, "refs/heads/", 11) == 0)
1344 branchname += 11;
1345 } else
1346 branchname = "*"; /* fall back to all branches */
1347 if (mirror_references) {
1348 if (asprintf(&branches,
1349 "\tfetch = refs/heads/%s:refs/heads/%s\n",
1350 branchname, branchname) == -1) {
1351 err = got_error_from_errno("asprintf");
1352 goto done;
1353 }
1354 } else if (asprintf(&branches,
1355 "\tfetch = refs/heads/%s:refs/remotes/%s/%s\n",
1356 branchname, GOT_FETCH_DEFAULT_REMOTE_NAME"origin",
1357 branchname) == -1) {
1358 err = got_error_from_errno("asprintf");
1359 goto done;
1360 }
1361 }
1362 if (!TAILQ_EMPTY(wanted_refs)(((wanted_refs)->tqh_first) == ((void *)0))) {
1363 struct got_pathlist_entry *pe;
1364 TAILQ_FOREACH(pe, wanted_refs, entry)for((pe) = ((wanted_refs)->tqh_first); (pe) != ((void *)0)
; (pe) = ((pe)->entry.tqe_next))
{
1365 char *s;
1366 const char *refname = pe->path;
1367 if (strncmp(refname, "refs/", 5) == 0)
1368 refname += 5;
1369 if (mirror_references) {
1370 if (asprintf(&s,
1371 "%s\tfetch = refs/%s:refs/%s\n",
1372 refs ? refs : "", refname, refname) == -1) {
1373 err = got_error_from_errno("asprintf");
1374 goto done;
1375 }
1376 } else if (asprintf(&s,
1377 "%s\tfetch = refs/%s:refs/remotes/%s/%s\n",
1378 refs ? refs : "",
1379 refname, GOT_FETCH_DEFAULT_REMOTE_NAME"origin",
1380 refname) == -1) {
1381 err = got_error_from_errno("asprintf");
1382 goto done;
1383 }
1384 free(refs);
1385 refs = s;
1386 }
1387 }
1388
1389 if (asprintf(&gitconfig,
1390 "[remote \"%s\"]\n"
1391 "\turl = %s\n"
1392 "%s"
1393 "%s"
1394 "\tfetch = refs/tags/*:refs/tags/*\n",
1395 GOT_FETCH_DEFAULT_REMOTE_NAME"origin", git_url, branches ? branches : "",
1396 refs ? refs : "") == -1) {
1397 err = got_error_from_errno("asprintf");
1398 goto done;
1399 }
1400 n = fwrite(gitconfig, 1, strlen(gitconfig), gitconfig_file);
1401 if (n != strlen(gitconfig)) {
1402 err = got_ferror(gitconfig_file, GOT_ERR_IO6);
1403 goto done;
1404 }
1405done:
1406 if (gitconfig_file && fclose(gitconfig_file) == EOF(-1) && err == NULL((void *)0))
1407 err = got_error_from_errno2("fclose", gitconfig_path);
1408 free(gitconfig_path);
1409 free(branches);
1410 return err;
1411}
1412
1413static const struct got_error *
1414create_config_files(const char *proto, const char *host, const char *port,
1415 const char *remote_repo_path, const char *git_url, int fetch_all_branches,
1416 int mirror_references, struct got_pathlist_head *symrefs,
1417 struct got_pathlist_head *wanted_branches,
1418 struct got_pathlist_head *wanted_refs, struct got_repository *repo)
1419{
1420 const struct got_error *err = NULL((void *)0);
1421 const char *default_branch = NULL((void *)0);
1422 struct got_pathlist_entry *pe;
1423
1424 /*
1425 * If we asked for a set of wanted branches then use the first
1426 * one of those.
1427 */
1428 if (!TAILQ_EMPTY(wanted_branches)(((wanted_branches)->tqh_first) == ((void *)0))) {
1429 pe = TAILQ_FIRST(wanted_branches)((wanted_branches)->tqh_first);
1430 default_branch = pe->path;
1431 } else {
1432 /* First HEAD ref listed by server is the default branch. */
1433 TAILQ_FOREACH(pe, symrefs, entry)for((pe) = ((symrefs)->tqh_first); (pe) != ((void *)0); (pe
) = ((pe)->entry.tqe_next))
{
1434 const char *refname = pe->path;
1435 const char *target = pe->data;
1436
1437 if (strcmp(refname, GOT_REF_HEAD"HEAD") != 0)
1438 continue;
1439
1440 default_branch = target;
1441 break;
1442 }
1443 }
1444
1445 /* Create got.conf(5). */
1446 err = create_gotconfig(proto, host, port, remote_repo_path,
1447 default_branch, fetch_all_branches, wanted_branches,
1448 wanted_refs, mirror_references, repo);
1449 if (err)
1450 return err;
1451
1452 /* Create a config file Git can understand. */
1453 return create_gitconfig(git_url, default_branch, fetch_all_branches,
1454 wanted_branches, wanted_refs, mirror_references, repo);
1455}
1456
1457static const struct got_error *
1458cmd_clone(int argc, char *argv[])
1459{
1460 const struct got_error *error = NULL((void *)0);
1461 const char *uri, *dirname;
1462 char *proto, *host, *port, *repo_name, *server_path;
1463 char *default_destdir = NULL((void *)0), *id_str = NULL((void *)0);
1464 const char *repo_path;
1465 struct got_repository *repo = NULL((void *)0);
1466 struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
1467 struct got_pathlist_entry *pe;
1468 struct got_object_id *pack_hash = NULL((void *)0);
1469 int ch, fetchfd = -1, fetchstatus;
1470 pid_t fetchpid = -1;
1471 struct got_fetch_progress_arg fpa;
1472 char *git_url = NULL((void *)0);
1473 int verbosity = 0, fetch_all_branches = 0, mirror_references = 0;
1474 int list_refs_only = 0;
1475
1476 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
1477 TAILQ_INIT(&symrefs)do { (&symrefs)->tqh_first = ((void *)0); (&symrefs
)->tqh_last = &(&symrefs)->tqh_first; } while (
0)
;
1478 TAILQ_INIT(&wanted_branches)do { (&wanted_branches)->tqh_first = ((void *)0); (&
wanted_branches)->tqh_last = &(&wanted_branches)->
tqh_first; } while (0)
;
1479 TAILQ_INIT(&wanted_refs)do { (&wanted_refs)->tqh_first = ((void *)0); (&wanted_refs
)->tqh_last = &(&wanted_refs)->tqh_first; } while
(0)
;
1480
1481 while ((ch = getopt(argc, argv, "ab:lmvqR:")) != -1) {
1482 switch (ch) {
1483 case 'a':
1484 fetch_all_branches = 1;
1485 break;
1486 case 'b':
1487 error = got_pathlist_append(&wanted_branches,
1488 optarg, NULL((void *)0));
1489 if (error)
1490 return error;
1491 break;
1492 case 'l':
1493 list_refs_only = 1;
1494 break;
1495 case 'm':
1496 mirror_references = 1;
1497 break;
1498 case 'v':
1499 if (verbosity < 0)
1500 verbosity = 0;
1501 else if (verbosity < 3)
1502 verbosity++;
1503 break;
1504 case 'q':
1505 verbosity = -1;
1506 break;
1507 case 'R':
1508 error = got_pathlist_append(&wanted_refs,
1509 optarg, NULL((void *)0));
1510 if (error)
1511 return error;
1512 break;
1513 default:
1514 usage_clone();
1515 break;
1516 }
1517 }
1518 argc -= optind;
1519 argv += optind;
1520
1521 if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches)(((&wanted_branches)->tqh_first) == ((void *)0)))
1522 option_conflict('a', 'b');
1523 if (list_refs_only) {
1524 if (!TAILQ_EMPTY(&wanted_branches)(((&wanted_branches)->tqh_first) == ((void *)0)))
1525 option_conflict('l', 'b');
1526 if (fetch_all_branches)
1527 option_conflict('l', 'a');
1528 if (mirror_references)
1529 option_conflict('l', 'm');
1530 if (!TAILQ_EMPTY(&wanted_refs)(((&wanted_refs)->tqh_first) == ((void *)0)))
1531 option_conflict('l', 'R');
1532 }
1533
1534 uri = argv[0];
1535
1536 if (argc == 1)
1537 dirname = NULL((void *)0);
1538 else if (argc == 2)
1539 dirname = argv[1];
1540 else
1541 usage_clone();
1542
1543 error = got_fetch_parse_uri(&proto, &host, &port, &server_path,
1544 &repo_name, uri);
1545 if (error)
1546 goto done;
1547
1548 if (asprintf(&git_url, "%s://%s%s%s%s%s", proto,
1549 host, port ? ":" : "", port ? port : "",
1550 server_path[0] != '/' ? "/" : "", server_path) == -1) {
1551 error = got_error_from_errno("asprintf");
1552 goto done;
1553 }
1554
1555 if (strcmp(proto, "git") == 0) {
1556#ifndef PROFILE
1557 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1558 "sendfd dns inet unveil", NULL((void *)0)) == -1)
1559 err(1, "pledge");
1560#endif
1561 } else if (strcmp(proto, "git+ssh") == 0 ||
1562 strcmp(proto, "ssh") == 0) {
1563#ifndef PROFILE
1564 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
1565 "sendfd unveil", NULL((void *)0)) == -1)
1566 err(1, "pledge");
1567#endif
1568 } else if (strcmp(proto, "http") == 0 ||
1569 strcmp(proto, "git+http") == 0) {
1570 error = got_error_path(proto, GOT_ERR_NOT_IMPL18);
1571 goto done;
1572 } else {
1573 error = got_error_path(proto, GOT_ERR_BAD_PROTO120);
1574 goto done;
1575 }
1576 if (dirname == NULL((void *)0)) {
1577 if (asprintf(&default_destdir, "%s.git", repo_name) == -1) {
1578 error = got_error_from_errno("asprintf");
1579 goto done;
1580 }
1581 repo_path = default_destdir;
1582 } else
1583 repo_path = dirname;
1584
1585 if (!list_refs_only) {
1586 error = got_path_mkdir(repo_path);
1587 if (error &&
1588 (!(error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EISDIR21) &&
1589 !(error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EEXIST17)))
1590 goto done;
1591 if (!got_path_dir_is_empty(repo_path)) {
1592 error = got_error_path(repo_path,
1593 GOT_ERR_DIR_NOT_EMPTY75);
1594 goto done;
1595 }
1596 }
1597
1598 if (strcmp(proto, "git+ssh") == 0 || strcmp(proto, "ssh") == 0) {
1599 if (unveil(GOT_FETCH_PATH_SSH"/usr/bin/ssh", "x") != 0) {
1600 error = got_error_from_errno2("unveil",
1601 GOT_FETCH_PATH_SSH"/usr/bin/ssh");
1602 goto done;
1603 }
1604 }
1605 error = apply_unveil(repo_path, 0, NULL((void *)0));
1606 if (error)
1607 goto done;
1608
1609 if (verbosity >= 0)
1610 printf("Connecting to %s%s%s\n", host,
1611 port ? ":" : "", port ? port : "");
1612
1613 error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port,
1614 server_path, verbosity);
1615 if (error)
1616 goto done;
1617
1618 if (!list_refs_only) {
1619 error = got_repo_init(repo_path);
1620 if (error)
1621 goto done;
1622 error = got_repo_open(&repo, repo_path, NULL((void *)0));
1623 if (error)
1624 goto done;
1625 }
1626
1627 fpa.last_scaled_size[0] = '\0';
1628 fpa.last_p_indexed = -1;
1629 fpa.last_p_resolved = -1;
1630 fpa.verbosity = verbosity;
1631 fpa.create_configs = 1;
1632 fpa.configs_created = 0;
1633 fpa.repo = repo;
1634 fpa.config_info.symrefs = &symrefs;
1635 fpa.config_info.wanted_branches = &wanted_branches;
1636 fpa.config_info.wanted_refs = &wanted_refs;
1637 fpa.config_info.proto = proto;
1638 fpa.config_info.host = host;
1639 fpa.config_info.port = port;
1640 fpa.config_info.remote_repo_path = server_path;
1641 fpa.config_info.git_url = git_url;
1642 fpa.config_info.fetch_all_branches = fetch_all_branches;
1643 fpa.config_info.mirror_references = mirror_references;
1644 error = got_fetch_pack(&pack_hash, &refs, &symrefs,
1645 GOT_FETCH_DEFAULT_REMOTE_NAME"origin", mirror_references,
1646 fetch_all_branches, &wanted_branches, &wanted_refs,
1647 list_refs_only, verbosity, fetchfd, repo,
1648 fetch_progress, &fpa);
1649 if (error)
1650 goto done;
1651
1652 if (list_refs_only) {
1653 error = list_remote_refs(&symrefs, &refs);
1654 goto done;
1655 }
1656
1657 error = got_object_id_str(&id_str, pack_hash);
1658 if (error)
1659 goto done;
1660 if (verbosity >= 0)
1661 printf("\nFetched %s.pack\n", id_str);
1662 free(id_str);
1663
1664 /* Set up references provided with the pack file. */
1665 TAILQ_FOREACH(pe, &refs, entry)for((pe) = ((&refs)->tqh_first); (pe) != ((void *)0); (
pe) = ((pe)->entry.tqe_next))
{
1666 const char *refname = pe->path;
1667 struct got_object_id *id = pe->data;
1668 char *remote_refname;
1669
1670 if (is_wanted_ref(&wanted_refs, refname) &&
1671 !mirror_references) {
1672 error = create_wanted_ref(refname, id,
1673 GOT_FETCH_DEFAULT_REMOTE_NAME"origin",
1674 verbosity - 1, repo);
1675 if (error)
1676 goto done;
1677 continue;
1678 }
1679
1680 error = create_ref(refname, id, verbosity - 1, repo);
1681 if (error)
1682 goto done;
1683
1684 if (mirror_references)
1685 continue;
1686
1687 if (strncmp("refs/heads/", refname, 11) != 0)
1688 continue;
1689
1690 if (asprintf(&remote_refname,
1691 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME"origin",
1692 refname + 11) == -1) {
1693 error = got_error_from_errno("asprintf");
1694 goto done;
1695 }
1696 error = create_ref(remote_refname, id, verbosity - 1, repo);
1697 free(remote_refname);
1698 if (error)
1699 goto done;
1700 }
1701
1702 /* Set the HEAD reference if the server provided one. */
1703 TAILQ_FOREACH(pe, &symrefs, entry)for((pe) = ((&symrefs)->tqh_first); (pe) != ((void *)0
); (pe) = ((pe)->entry.tqe_next))
{
1704 struct got_reference *target_ref;
1705 const char *refname = pe->path;
1706 const char *target = pe->data;
1707 char *remote_refname = NULL((void *)0), *remote_target = NULL((void *)0);
1708
1709 if (strcmp(refname, GOT_REF_HEAD"HEAD") != 0)
1710 continue;
1711
1712 error = got_ref_open(&target_ref, repo, target, 0);
1713 if (error) {
1714 if (error->code == GOT_ERR_NOT_REF5) {
1715 error = NULL((void *)0);
1716 continue;
1717 }
1718 goto done;
1719 }
1720
1721 error = create_symref(refname, target_ref, verbosity, repo);
1722 got_ref_close(target_ref);
1723 if (error)
1724 goto done;
1725
1726 if (mirror_references)
1727 continue;
1728
1729 if (strncmp("refs/heads/", target, 11) != 0)
1730 continue;
1731
1732 if (asprintf(&remote_refname,
1733 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME"origin",
1734 refname) == -1) {
1735 error = got_error_from_errno("asprintf");
1736 goto done;
1737 }
1738 if (asprintf(&remote_target,
1739 "refs/remotes/%s/%s", GOT_FETCH_DEFAULT_REMOTE_NAME"origin",
1740 target + 11) == -1) {
1741 error = got_error_from_errno("asprintf");
1742 free(remote_refname);
1743 goto done;
1744 }
1745 error = got_ref_open(&target_ref, repo, remote_target, 0);
1746 if (error) {
1747 free(remote_refname);
1748 free(remote_target);
1749 if (error->code == GOT_ERR_NOT_REF5) {
1750 error = NULL((void *)0);
1751 continue;
1752 }
1753 goto done;
1754 }
1755 error = create_symref(remote_refname, target_ref,
1756 verbosity - 1, repo);
1757 free(remote_refname);
1758 free(remote_target);
1759 got_ref_close(target_ref);
1760 if (error)
1761 goto done;
1762 }
1763 if (pe == NULL((void *)0)) {
1764 /*
1765 * We failed to set the HEAD reference. If we asked for
1766 * a set of wanted branches use the first of one of those
1767 * which could be fetched instead.
1768 */
1769 TAILQ_FOREACH(pe, &wanted_branches, entry)for((pe) = ((&wanted_branches)->tqh_first); (pe) != ((
void *)0); (pe) = ((pe)->entry.tqe_next))
{
1770 const char *target = pe->path;
1771 struct got_reference *target_ref;
1772
1773 error = got_ref_open(&target_ref, repo, target, 0);
1774 if (error) {
1775 if (error->code == GOT_ERR_NOT_REF5) {
1776 error = NULL((void *)0);
1777 continue;
1778 }
1779 goto done;
1780 }
1781
1782 error = create_symref(GOT_REF_HEAD"HEAD", target_ref,
1783 verbosity, repo);
1784 got_ref_close(target_ref);
1785 if (error)
1786 goto done;
1787 break;
1788 }
1789 }
1790
1791 if (verbosity >= 0)
1792 printf("Created %s repository '%s'\n",
1793 mirror_references ? "mirrored" : "cloned", repo_path);
1794done:
1795 if (fetchpid > 0) {
1796 if (kill(fetchpid, SIGTERM15) == -1)
1797 error = got_error_from_errno("kill");
1798 if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL((void *)0))
1799 error = got_error_from_errno("waitpid");
1800 }
1801 if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL((void *)0))
1802 error = got_error_from_errno("close");
1803 if (repo)
1804 got_repo_close(repo);
1805 TAILQ_FOREACH(pe, &refs, entry)for((pe) = ((&refs)->tqh_first); (pe) != ((void *)0); (
pe) = ((pe)->entry.tqe_next))
{
1806 free((void *)pe->path);
1807 free(pe->data);
1808 }
1809 got_pathlist_free(&refs);
1810 TAILQ_FOREACH(pe, &symrefs, entry)for((pe) = ((&symrefs)->tqh_first); (pe) != ((void *)0
); (pe) = ((pe)->entry.tqe_next))
{
1811 free((void *)pe->path);
1812 free(pe->data);
1813 }
1814 got_pathlist_free(&symrefs);
1815 got_pathlist_free(&wanted_branches);
1816 got_pathlist_free(&wanted_refs);
1817 free(pack_hash);
1818 free(proto);
1819 free(host);
1820 free(port);
1821 free(server_path);
1822 free(repo_name);
1823 free(default_destdir);
1824 free(git_url);
1825 return error;
1826}
1827
1828static const struct got_error *
1829update_ref(struct got_reference *ref, struct got_object_id *new_id,
1830 int replace_tags, int verbosity, struct got_repository *repo)
1831{
1832 const struct got_error *err = NULL((void *)0);
1833 char *new_id_str = NULL((void *)0);
1834 struct got_object_id *old_id = NULL((void *)0);
1835
1836 err = got_object_id_str(&new_id_str, new_id);
1837 if (err)
1838 goto done;
1839
1840 if (!replace_tags &&
1841 strncmp(got_ref_get_name(ref), "refs/tags/", 10) == 0) {
1842 err = got_ref_resolve(&old_id, repo, ref);
1843 if (err)
1844 goto done;
1845 if (got_object_id_cmp(old_id, new_id) == 0)
1846 goto done;
1847 if (verbosity >= 0) {
1848 printf("Rejecting update of existing tag %s: %s\n",
1849 got_ref_get_name(ref), new_id_str);
1850 }
1851 goto done;
1852 }
1853
1854 if (got_ref_is_symbolic(ref)) {
1855 if (verbosity >= 0) {
1856 printf("Replacing reference %s: %s\n",
1857 got_ref_get_name(ref),
1858 got_ref_get_symref_target(ref));
1859 }
1860 err = got_ref_change_symref_to_ref(ref, new_id);
1861 if (err)
1862 goto done;
1863 err = got_ref_write(ref, repo);
1864 if (err)
1865 goto done;
1866 } else {
1867 err = got_ref_resolve(&old_id, repo, ref);
1868 if (err)
1869 goto done;
1870 if (got_object_id_cmp(old_id, new_id) == 0)
1871 goto done;
1872
1873 err = got_ref_change_ref(ref, new_id);
1874 if (err)
1875 goto done;
1876 err = got_ref_write(ref, repo);
1877 if (err)
1878 goto done;
1879 }
1880
1881 if (verbosity >= 0)
1882 printf("Updated %s: %s\n", got_ref_get_name(ref),
1883 new_id_str);
1884done:
1885 free(old_id);
1886 free(new_id_str);
1887 return err;
1888}
1889
1890static const struct got_error *
1891update_symref(const char *refname, struct got_reference *target_ref,
1892 int verbosity, struct got_repository *repo)
1893{
1894 const struct got_error *err = NULL((void *)0), *unlock_err;
1895 struct got_reference *symref;
1896 int symref_is_locked = 0;
1897
1898 err = got_ref_open(&symref, repo, refname, 1);
1899 if (err) {
1900 if (err->code != GOT_ERR_NOT_REF5)
1901 return err;
1902 err = got_ref_alloc_symref(&symref, refname, target_ref);
1903 if (err)
1904 goto done;
1905
1906 err = got_ref_write(symref, repo);
1907 if (err)
1908 goto done;
1909
1910 if (verbosity >= 0)
1911 printf("Created reference %s: %s\n",
1912 got_ref_get_name(symref),
1913 got_ref_get_symref_target(symref));
1914 } else {
1915 symref_is_locked = 1;
1916
1917 if (strcmp(got_ref_get_symref_target(symref),
1918 got_ref_get_name(target_ref)) == 0)
1919 goto done;
1920
1921 err = got_ref_change_symref(symref,
1922 got_ref_get_name(target_ref));
1923 if (err)
1924 goto done;
1925
1926 err = got_ref_write(symref, repo);
1927 if (err)
1928 goto done;
1929
1930 if (verbosity >= 0)
1931 printf("Updated %s: %s\n", got_ref_get_name(symref),
1932 got_ref_get_symref_target(symref));
1933
1934 }
1935done:
1936 if (symref_is_locked) {
1937 unlock_err = got_ref_unlock(symref);
1938 if (unlock_err && err == NULL((void *)0))
1939 err = unlock_err;
1940 }
1941 got_ref_close(symref);
1942 return err;
1943}
1944
1945__dead__attribute__((__noreturn__)) static void
1946usage_fetch(void)
1947{
1948 fprintf(stderr(&__sF[2]), "usage: %s fetch [-a] [-b branch] [-d] [-l] "
1949 "[-r repository-path] [-t] [-q] [-v] [-R reference] "
1950 "[remote-repository-name]\n",
1951 getprogname());
1952 exit(1);
1953}
1954
1955static const struct got_error *
1956delete_missing_ref(struct got_reference *ref,
1957 int verbosity, struct got_repository *repo)
1958{
1959 const struct got_error *err = NULL((void *)0);
1960 struct got_object_id *id = NULL((void *)0);
1961 char *id_str = NULL((void *)0);
1962
1963 if (got_ref_is_symbolic(ref)) {
1964 err = got_ref_delete(ref, repo);
1965 if (err)
1966 return err;
1967 if (verbosity >= 0) {
1968 printf("Deleted reference %s: %s\n",
1969 got_ref_get_name(ref),
1970 got_ref_get_symref_target(ref));
1971 }
1972 } else {
1973 err = got_ref_resolve(&id, repo, ref);
1974 if (err)
1975 return err;
1976 err = got_object_id_str(&id_str, id);
1977 if (err)
1978 goto done;
1979
1980 err = got_ref_delete(ref, repo);
1981 if (err)
1982 goto done;
1983 if (verbosity >= 0) {
1984 printf("Deleted reference %s: %s\n",
1985 got_ref_get_name(ref), id_str);
1986 }
1987 }
1988done:
1989 free(id);
1990 free(id_str);
1991 return NULL((void *)0);
1992}
1993
1994static const struct got_error *
1995delete_missing_refs(struct got_pathlist_head *their_refs,
1996 struct got_pathlist_head *their_symrefs,
1997 const struct got_remote_repo *remote,
1998 int verbosity, struct got_repository *repo)
1999{
2000 const struct got_error *err = NULL((void *)0), *unlock_err;
2001 struct got_reflist_head my_refs;
2002 struct got_reflist_entry *re;
2003 struct got_pathlist_entry *pe;
2004 char *remote_namespace = NULL((void *)0);
2005 char *local_refname = NULL((void *)0);
2006
2007 TAILQ_INIT(&my_refs)do { (&my_refs)->tqh_first = ((void *)0); (&my_refs
)->tqh_last = &(&my_refs)->tqh_first; } while (
0)
;
2008
2009 if (asprintf(&remote_namespace, "refs/remotes/%s/", remote->name)
2010 == -1)
2011 return got_error_from_errno("asprintf");
2012
2013 err = got_ref_list(&my_refs, repo, NULL((void *)0), got_ref_cmp_by_name, NULL((void *)0));
2014 if (err)
2015 goto done;
2016
2017 TAILQ_FOREACH(re, &my_refs, entry)for((re) = ((&my_refs)->tqh_first); (re) != ((void *)0
); (re) = ((re)->entry.tqe_next))
{
2018 const char *refname = got_ref_get_name(re->ref);
2019
2020 if (!remote->mirror_references) {
2021 if (strncmp(refname, remote_namespace,
2022 strlen(remote_namespace)) == 0) {
2023 if (strcmp(refname + strlen(remote_namespace),
2024 GOT_REF_HEAD"HEAD") == 0)
2025 continue;
2026 if (asprintf(&local_refname, "refs/heads/%s",
2027 refname + strlen(remote_namespace)) == -1) {
2028 err = got_error_from_errno("asprintf");
2029 goto done;
2030 }
2031 } else if (strncmp(refname, "refs/tags/", 10) != 0)
2032 continue;
2033 }
2034
2035 TAILQ_FOREACH(pe, their_refs, entry)for((pe) = ((their_refs)->tqh_first); (pe) != ((void *)0);
(pe) = ((pe)->entry.tqe_next))
{
2036 if (strcmp(local_refname, pe->path) == 0)
2037 break;
2038 }
2039 if (pe != NULL((void *)0))
2040 continue;
2041
2042 TAILQ_FOREACH(pe, their_symrefs, entry)for((pe) = ((their_symrefs)->tqh_first); (pe) != ((void *)
0); (pe) = ((pe)->entry.tqe_next))
{
2043 if (strcmp(local_refname, pe->path) == 0)
2044 break;
2045 }
2046 if (pe != NULL((void *)0))
2047 continue;
2048
2049 err = delete_missing_ref(re->ref, verbosity, repo);
2050 if (err)
2051 break;
2052
2053 if (local_refname) {
2054 struct got_reference *ref;
2055 err = got_ref_open(&ref, repo, local_refname, 1);
2056 if (err) {
2057 if (err->code != GOT_ERR_NOT_REF5)
2058 break;
2059 free(local_refname);
2060 local_refname = NULL((void *)0);
2061 continue;
2062 }
2063 err = delete_missing_ref(ref, verbosity, repo);
2064 if (err)
2065 break;
2066 unlock_err = got_ref_unlock(ref);
2067 got_ref_close(ref);
2068 if (unlock_err && err == NULL((void *)0)) {
2069 err = unlock_err;
2070 break;
2071 }
2072
2073 free(local_refname);
2074 local_refname = NULL((void *)0);
2075 }
2076 }
2077done:
2078 free(remote_namespace);
2079 free(local_refname);
2080 return err;
2081}
2082
2083static const struct got_error *
2084update_wanted_ref(const char *refname, struct got_object_id *id,
2085 const char *remote_repo_name, int verbosity, struct got_repository *repo)
2086{
2087 const struct got_error *err, *unlock_err;
2088 char *remote_refname;
2089 struct got_reference *ref;
2090
2091 if (strncmp("refs/", refname, 5) == 0)
2092 refname += 5;
2093
2094 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2095 remote_repo_name, refname) == -1)
2096 return got_error_from_errno("asprintf");
2097
2098 err = got_ref_open(&ref, repo, remote_refname, 1);
2099 if (err) {
2100 if (err->code != GOT_ERR_NOT_REF5)
2101 goto done;
2102 err = create_ref(remote_refname, id, verbosity, repo);
2103 } else {
2104 err = update_ref(ref, id, 0, verbosity, repo);
2105 unlock_err = got_ref_unlock(ref);
2106 if (unlock_err && err == NULL((void *)0))
2107 err = unlock_err;
2108 got_ref_close(ref);
2109 }
2110done:
2111 free(remote_refname);
2112 return err;
2113}
2114
2115static const struct got_error *
2116cmd_fetch(int argc, char *argv[])
2117{
2118 const struct got_error *error = NULL((void *)0), *unlock_err;
2119 char *cwd = NULL((void *)0), *repo_path = NULL((void *)0);
2120 const char *remote_name;
2121 char *proto = NULL((void *)0), *host = NULL((void *)0), *port = NULL((void *)0);
2122 char *repo_name = NULL((void *)0), *server_path = NULL((void *)0);
2123 const struct got_remote_repo *remotes, *remote = NULL((void *)0);
2124 int nremotes;
2125 char *id_str = NULL((void *)0);
2126 struct got_repository *repo = NULL((void *)0);
2127 struct got_worktree *worktree = NULL((void *)0);
2128 const struct got_gotconfig *repo_conf = NULL((void *)0), *worktree_conf = NULL((void *)0);
2129 struct got_pathlist_head refs, symrefs, wanted_branches, wanted_refs;
2130 struct got_pathlist_entry *pe;
2131 struct got_object_id *pack_hash = NULL((void *)0);
2132 int i, ch, fetchfd = -1, fetchstatus;
2133 pid_t fetchpid = -1;
2134 struct got_fetch_progress_arg fpa;
2135 int verbosity = 0, fetch_all_branches = 0, list_refs_only = 0;
2136 int delete_refs = 0, replace_tags = 0;
2137
2138 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
2139 TAILQ_INIT(&symrefs)do { (&symrefs)->tqh_first = ((void *)0); (&symrefs
)->tqh_last = &(&symrefs)->tqh_first; } while (
0)
;
2140 TAILQ_INIT(&wanted_branches)do { (&wanted_branches)->tqh_first = ((void *)0); (&
wanted_branches)->tqh_last = &(&wanted_branches)->
tqh_first; } while (0)
;
2141 TAILQ_INIT(&wanted_refs)do { (&wanted_refs)->tqh_first = ((void *)0); (&wanted_refs
)->tqh_last = &(&wanted_refs)->tqh_first; } while
(0)
;
2142
2143 while ((ch = getopt(argc, argv, "ab:dlr:tvqR:")) != -1) {
2144 switch (ch) {
2145 case 'a':
2146 fetch_all_branches = 1;
2147 break;
2148 case 'b':
2149 error = got_pathlist_append(&wanted_branches,
2150 optarg, NULL((void *)0));
2151 if (error)
2152 return error;
2153 break;
2154 case 'd':
2155 delete_refs = 1;
2156 break;
2157 case 'l':
2158 list_refs_only = 1;
2159 break;
2160 case 'r':
2161 repo_path = realpath(optarg, NULL((void *)0));
2162 if (repo_path == NULL((void *)0))
2163 return got_error_from_errno2("realpath",
2164 optarg);
2165 got_path_strip_trailing_slashes(repo_path);
2166 break;
2167 case 't':
2168 replace_tags = 1;
2169 break;
2170 case 'v':
2171 if (verbosity < 0)
2172 verbosity = 0;
2173 else if (verbosity < 3)
2174 verbosity++;
2175 break;
2176 case 'q':
2177 verbosity = -1;
2178 break;
2179 case 'R':
2180 error = got_pathlist_append(&wanted_refs,
2181 optarg, NULL((void *)0));
2182 if (error)
2183 return error;
2184 break;
2185 default:
2186 usage_fetch();
2187 break;
2188 }
2189 }
2190 argc -= optind;
2191 argv += optind;
2192
2193 if (fetch_all_branches && !TAILQ_EMPTY(&wanted_branches)(((&wanted_branches)->tqh_first) == ((void *)0)))
2194 option_conflict('a', 'b');
2195 if (list_refs_only) {
2196 if (!TAILQ_EMPTY(&wanted_branches)(((&wanted_branches)->tqh_first) == ((void *)0)))
2197 option_conflict('l', 'b');
2198 if (fetch_all_branches)
2199 option_conflict('l', 'a');
2200 if (delete_refs)
2201 option_conflict('l', 'd');
2202 }
2203
2204 if (argc == 0)
2205 remote_name = GOT_FETCH_DEFAULT_REMOTE_NAME"origin";
2206 else if (argc == 1)
2207 remote_name = argv[0];
2208 else
2209 usage_fetch();
2210
2211 cwd = getcwd(NULL((void *)0), 0);
2212 if (cwd == NULL((void *)0)) {
2213 error = got_error_from_errno("getcwd");
2214 goto done;
2215 }
2216
2217 if (repo_path == NULL((void *)0)) {
2218 error = got_worktree_open(&worktree, cwd);
2219 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
2220 goto done;
2221 else
2222 error = NULL((void *)0);
2223 if (worktree) {
2224 repo_path =
2225 strdup(got_worktree_get_repo_path(worktree));
2226 if (repo_path == NULL((void *)0))
2227 error = got_error_from_errno("strdup");
2228 if (error)
2229 goto done;
2230 } else {
2231 repo_path = strdup(cwd);
2232 if (repo_path == NULL((void *)0)) {
2233 error = got_error_from_errno("strdup");
2234 goto done;
2235 }
2236 }
2237 }
2238
2239 error = got_repo_open(&repo, repo_path, NULL((void *)0));
2240 if (error)
2241 goto done;
2242
2243 if (worktree) {
2244 worktree_conf = got_worktree_get_gotconfig(worktree);
2245 if (worktree_conf) {
2246 got_gotconfig_get_remotes(&nremotes, &remotes,
2247 worktree_conf);
2248 for (i = 0; i < nremotes; i++) {
2249 if (strcmp(remotes[i].name, remote_name) == 0) {
2250 remote = &remotes[i];
2251 break;
2252 }
2253 }
2254 }
2255 }
2256 if (remote == NULL((void *)0)) {
2257 repo_conf = got_repo_get_gotconfig(repo);
2258 if (repo_conf) {
2259 got_gotconfig_get_remotes(&nremotes, &remotes,
2260 repo_conf);
2261 for (i = 0; i < nremotes; i++) {
2262 if (strcmp(remotes[i].name, remote_name) == 0) {
2263 remote = &remotes[i];
2264 break;
2265 }
2266 }
2267 }
2268 }
2269 if (remote == NULL((void *)0)) {
2270 got_repo_get_gitconfig_remotes(&nremotes, &remotes, repo);
2271 for (i = 0; i < nremotes; i++) {
2272 if (strcmp(remotes[i].name, remote_name) == 0) {
2273 remote = &remotes[i];
2274 break;
2275 }
2276 }
2277 }
2278 if (remote == NULL((void *)0)) {
2279 error = got_error_path(remote_name, GOT_ERR_NO_REMOTE123);
2280 goto done;
2281 }
2282
2283 if (TAILQ_EMPTY(&wanted_branches)(((&wanted_branches)->tqh_first) == ((void *)0))) {
2284 if (!fetch_all_branches)
2285 fetch_all_branches = remote->fetch_all_branches;
2286 for (i = 0; i < remote->nbranches; i++) {
2287 got_pathlist_append(&wanted_branches,
2288 remote->branches[i], NULL((void *)0));
2289 }
2290 }
2291 if (TAILQ_EMPTY(&wanted_refs)(((&wanted_refs)->tqh_first) == ((void *)0))) {
2292 for (i = 0; i < remote->nrefs; i++) {
2293 got_pathlist_append(&wanted_refs,
2294 remote->refs[i], NULL((void *)0));
2295 }
2296 }
2297
2298 error = got_fetch_parse_uri(&proto, &host, &port, &server_path,
2299 &repo_name, remote->url);
2300 if (error)
2301 goto done;
2302
2303 if (strcmp(proto, "git") == 0) {
2304#ifndef PROFILE
2305 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2306 "sendfd dns inet unveil", NULL((void *)0)) == -1)
2307 err(1, "pledge");
2308#endif
2309 } else if (strcmp(proto, "git+ssh") == 0 ||
2310 strcmp(proto, "ssh") == 0) {
2311#ifndef PROFILE
2312 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
2313 "sendfd unveil", NULL((void *)0)) == -1)
2314 err(1, "pledge");
2315#endif
2316 } else if (strcmp(proto, "http") == 0 ||
2317 strcmp(proto, "git+http") == 0) {
2318 error = got_error_path(proto, GOT_ERR_NOT_IMPL18);
2319 goto done;
2320 } else {
2321 error = got_error_path(proto, GOT_ERR_BAD_PROTO120);
2322 goto done;
2323 }
2324
2325 if (strcmp(proto, "git+ssh") == 0 || strcmp(proto, "ssh") == 0) {
2326 if (unveil(GOT_FETCH_PATH_SSH"/usr/bin/ssh", "x") != 0) {
2327 error = got_error_from_errno2("unveil",
2328 GOT_FETCH_PATH_SSH"/usr/bin/ssh");
2329 goto done;
2330 }
2331 }
2332 error = apply_unveil(got_repo_get_path(repo), 0, NULL((void *)0));
2333 if (error)
2334 goto done;
2335
2336 if (verbosity >= 0)
2337 printf("Connecting to \"%s\" %s%s%s\n", remote->name, host,
2338 port ? ":" : "", port ? port : "");
2339
2340 error = got_fetch_connect(&fetchpid, &fetchfd, proto, host, port,
2341 server_path, verbosity);
2342 if (error)
2343 goto done;
2344
2345 fpa.last_scaled_size[0] = '\0';
2346 fpa.last_p_indexed = -1;
2347 fpa.last_p_resolved = -1;
2348 fpa.verbosity = verbosity;
2349 fpa.repo = repo;
2350 fpa.create_configs = 0;
2351 fpa.configs_created = 0;
2352 memset(&fpa.config_info, 0, sizeof(fpa.config_info));
2353 error = got_fetch_pack(&pack_hash, &refs, &symrefs, remote->name,
2354 remote->mirror_references, fetch_all_branches, &wanted_branches,
2355 &wanted_refs, list_refs_only, verbosity, fetchfd, repo,
2356 fetch_progress, &fpa);
2357 if (error)
2358 goto done;
2359
2360 if (list_refs_only) {
2361 error = list_remote_refs(&symrefs, &refs);
2362 goto done;
2363 }
2364
2365 if (pack_hash == NULL((void *)0)) {
2366 if (verbosity >= 0)
2367 printf("Already up-to-date\n");
2368 } else if (verbosity >= 0) {
2369 error = got_object_id_str(&id_str, pack_hash);
2370 if (error)
2371 goto done;
2372 printf("\nFetched %s.pack\n", id_str);
2373 free(id_str);
2374 id_str = NULL((void *)0);
2375 }
2376
2377 /* Update references provided with the pack file. */
2378 TAILQ_FOREACH(pe, &refs, entry)for((pe) = ((&refs)->tqh_first); (pe) != ((void *)0); (
pe) = ((pe)->entry.tqe_next))
{
2379 const char *refname = pe->path;
2380 struct got_object_id *id = pe->data;
2381 struct got_reference *ref;
2382 char *remote_refname;
2383
2384 if (is_wanted_ref(&wanted_refs, refname) &&
2385 !remote->mirror_references) {
2386 error = update_wanted_ref(refname, id,
2387 remote->name, verbosity, repo);
2388 if (error)
2389 goto done;
2390 continue;
2391 }
2392
2393 if (remote->mirror_references ||
2394 strncmp("refs/tags/", refname, 10) == 0) {
2395 error = got_ref_open(&ref, repo, refname, 1);
2396 if (error) {
2397 if (error->code != GOT_ERR_NOT_REF5)
2398 goto done;
2399 error = create_ref(refname, id, verbosity,
2400 repo);
2401 if (error)
2402 goto done;
2403 } else {
2404 error = update_ref(ref, id, replace_tags,
2405 verbosity, repo);
2406 unlock_err = got_ref_unlock(ref);
2407 if (unlock_err && error == NULL((void *)0))
2408 error = unlock_err;
2409 got_ref_close(ref);
2410 if (error)
2411 goto done;
2412 }
2413 } else if (strncmp("refs/heads/", refname, 11) == 0) {
2414 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2415 remote_name, refname + 11) == -1) {
2416 error = got_error_from_errno("asprintf");
2417 goto done;
2418 }
2419
2420 error = got_ref_open(&ref, repo, remote_refname, 1);
2421 if (error) {
2422 if (error->code != GOT_ERR_NOT_REF5)
2423 goto done;
2424 error = create_ref(remote_refname, id,
2425 verbosity, repo);
2426 if (error)
2427 goto done;
2428 } else {
2429 error = update_ref(ref, id, replace_tags,
2430 verbosity, repo);
2431 unlock_err = got_ref_unlock(ref);
2432 if (unlock_err && error == NULL((void *)0))
2433 error = unlock_err;
2434 got_ref_close(ref);
2435 if (error)
2436 goto done;
2437 }
2438
2439 /* Also create a local branch if none exists yet. */
2440 error = got_ref_open(&ref, repo, refname, 1);
2441 if (error) {
2442 if (error->code != GOT_ERR_NOT_REF5)
2443 goto done;
2444 error = create_ref(refname, id, verbosity,
2445 repo);
2446 if (error)
2447 goto done;
2448 } else {
2449 unlock_err = got_ref_unlock(ref);
2450 if (unlock_err && error == NULL((void *)0))
2451 error = unlock_err;
2452 got_ref_close(ref);
2453 }
2454 }
2455 }
2456 if (delete_refs) {
2457 error = delete_missing_refs(&refs, &symrefs, remote,
2458 verbosity, repo);
2459 if (error)
2460 goto done;
2461 }
2462
2463 if (!remote->mirror_references) {
2464 /* Update remote HEAD reference if the server provided one. */
2465 TAILQ_FOREACH(pe, &symrefs, entry)for((pe) = ((&symrefs)->tqh_first); (pe) != ((void *)0
); (pe) = ((pe)->entry.tqe_next))
{
2466 struct got_reference *target_ref;
2467 const char *refname = pe->path;
2468 const char *target = pe->data;
2469 char *remote_refname = NULL((void *)0), *remote_target = NULL((void *)0);
2470
2471 if (strcmp(refname, GOT_REF_HEAD"HEAD") != 0)
2472 continue;
2473
2474 if (strncmp("refs/heads/", target, 11) != 0)
2475 continue;
2476
2477 if (asprintf(&remote_refname, "refs/remotes/%s/%s",
2478 remote->name, refname) == -1) {
2479 error = got_error_from_errno("asprintf");
2480 goto done;
2481 }
2482 if (asprintf(&remote_target, "refs/remotes/%s/%s",
2483 remote->name, target + 11) == -1) {
2484 error = got_error_from_errno("asprintf");
2485 free(remote_refname);
2486 goto done;
2487 }
2488
2489 error = got_ref_open(&target_ref, repo, remote_target,
2490 0);
2491 if (error) {
2492 free(remote_refname);
2493 free(remote_target);
2494 if (error->code == GOT_ERR_NOT_REF5) {
2495 error = NULL((void *)0);
2496 continue;
2497 }
2498 goto done;
2499 }
2500 error = update_symref(remote_refname, target_ref,
2501 verbosity, repo);
2502 free(remote_refname);
2503 free(remote_target);
2504 got_ref_close(target_ref);
2505 if (error)
2506 goto done;
2507 }
2508 }
2509done:
2510 if (fetchpid > 0) {
2511 if (kill(fetchpid, SIGTERM15) == -1)
2512 error = got_error_from_errno("kill");
2513 if (waitpid(fetchpid, &fetchstatus, 0) == -1 && error == NULL((void *)0))
2514 error = got_error_from_errno("waitpid");
2515 }
2516 if (fetchfd != -1 && close(fetchfd) == -1 && error == NULL((void *)0))
2517 error = got_error_from_errno("close");
2518 if (repo)
2519 got_repo_close(repo);
2520 if (worktree)
2521 got_worktree_close(worktree);
2522 TAILQ_FOREACH(pe, &refs, entry)for((pe) = ((&refs)->tqh_first); (pe) != ((void *)0); (
pe) = ((pe)->entry.tqe_next))
{
2523 free((void *)pe->path);
2524 free(pe->data);
2525 }
2526 got_pathlist_free(&refs);
2527 TAILQ_FOREACH(pe, &symrefs, entry)for((pe) = ((&symrefs)->tqh_first); (pe) != ((void *)0
); (pe) = ((pe)->entry.tqe_next))
{
2528 free((void *)pe->path);
2529 free(pe->data);
2530 }
2531 got_pathlist_free(&symrefs);
2532 got_pathlist_free(&wanted_branches);
2533 got_pathlist_free(&wanted_refs);
2534 free(id_str);
2535 free(cwd);
2536 free(repo_path);
2537 free(pack_hash);
2538 free(proto);
2539 free(host);
2540 free(port);
2541 free(server_path);
2542 free(repo_name);
2543 return error;
2544}
2545
2546
2547__dead__attribute__((__noreturn__)) static void
2548usage_checkout(void)
2549{
2550 fprintf(stderr(&__sF[2]), "usage: %s checkout [-E] [-b branch] [-c commit] "
2551 "[-p prefix] repository-path [worktree-path]\n", getprogname());
2552 exit(1);
2553}
2554
2555static void
2556show_worktree_base_ref_warning(void)
2557{
2558 fprintf(stderr(&__sF[2]), "%s: warning: could not create a reference "
2559 "to the work tree's base commit; the commit could be "
2560 "garbage-collected by Git; making the repository "
2561 "writable and running 'got update' will prevent this\n",
2562 getprogname());
2563}
2564
2565struct got_checkout_progress_arg {
2566 const char *worktree_path;
2567 int had_base_commit_ref_error;
2568};
2569
2570static const struct got_error *
2571checkout_progress(void *arg, unsigned char status, const char *path)
2572{
2573 struct got_checkout_progress_arg *a = arg;
2574
2575 /* Base commit bump happens silently. */
2576 if (status == GOT_STATUS_BUMP_BASE'b')
2577 return NULL((void *)0);
2578
2579 if (status == GOT_STATUS_BASE_REF_ERR'B') {
2580 a->had_base_commit_ref_error = 1;
2581 return NULL((void *)0);
2582 }
2583
2584 while (path[0] == '/')
2585 path++;
2586
2587 printf("%c %s/%s\n", status, a->worktree_path, path);
2588 return NULL((void *)0);
2589}
2590
2591static const struct got_error *
2592check_cancelled(void *arg)
2593{
2594 if (sigint_received || sigpipe_received)
2595 return got_error(GOT_ERR_CANCELLED49);
2596 return NULL((void *)0);
2597}
2598
2599static const struct got_error *
2600check_linear_ancestry(struct got_object_id *commit_id,
2601 struct got_object_id *base_commit_id, int allow_forwards_in_time_only,
2602 struct got_repository *repo)
2603{
2604 const struct got_error *err = NULL((void *)0);
2605 struct got_object_id *yca_id;
2606
2607 err = got_commit_graph_find_youngest_common_ancestor(&yca_id,
2608 commit_id, base_commit_id, repo, check_cancelled, NULL((void *)0));
2609 if (err)
2610 return err;
2611
2612 if (yca_id == NULL((void *)0))
2613 return got_error(GOT_ERR_ANCESTRY55);
2614
2615 /*
2616 * Require a straight line of history between the target commit
2617 * and the work tree's base commit.
2618 *
2619 * Non-linear situations such as this require a rebase:
2620 *
2621 * (commit) D F (base_commit)
2622 * \ /
2623 * C E
2624 * \ /
2625 * B (yca)
2626 * |
2627 * A
2628 *
2629 * 'got update' only handles linear cases:
2630 * Update forwards in time: A (base/yca) - B - C - D (commit)
2631 * Update backwards in time: D (base) - C - B - A (commit/yca)
2632 */
2633 if (allow_forwards_in_time_only) {
2634 if (got_object_id_cmp(base_commit_id, yca_id) != 0)
2635 return got_error(GOT_ERR_ANCESTRY55);
2636 } else if (got_object_id_cmp(commit_id, yca_id) != 0 &&
2637 got_object_id_cmp(base_commit_id, yca_id) != 0)
2638 return got_error(GOT_ERR_ANCESTRY55);
2639
2640 free(yca_id);
2641 return NULL((void *)0);
2642}
2643
2644static const struct got_error *
2645check_same_branch(struct got_object_id *commit_id,
2646 struct got_reference *head_ref, struct got_object_id *yca_id,
2647 struct got_repository *repo)
2648{
2649 const struct got_error *err = NULL((void *)0);
2650 struct got_commit_graph *graph = NULL((void *)0);
2651 struct got_object_id *head_commit_id = NULL((void *)0);
2652 int is_same_branch = 0;
2653
2654 err = got_ref_resolve(&head_commit_id, repo, head_ref);
2655 if (err)
2656 goto done;
2657
2658 if (got_object_id_cmp(head_commit_id, commit_id) == 0) {
2659 is_same_branch = 1;
2660 goto done;
2661 }
2662 if (yca_id && got_object_id_cmp(commit_id, yca_id) == 0) {
2663 is_same_branch = 1;
2664 goto done;
2665 }
2666
2667 err = got_commit_graph_open(&graph, "/", 1);
2668 if (err)
2669 goto done;
2670
2671 err = got_commit_graph_iter_start(graph, head_commit_id, repo,
2672 check_cancelled, NULL((void *)0));
2673 if (err)
2674 goto done;
2675
2676 for (;;) {
2677 struct got_object_id *id;
2678 err = got_commit_graph_iter_next(&id, graph, repo,
2679 check_cancelled, NULL((void *)0));
2680 if (err) {
2681 if (err->code == GOT_ERR_ITER_COMPLETED46)
2682 err = NULL((void *)0);
2683 break;
2684 }
2685
2686 if (id) {
2687 if (yca_id && got_object_id_cmp(id, yca_id) == 0)
2688 break;
2689 if (got_object_id_cmp(id, commit_id) == 0) {
2690 is_same_branch = 1;
2691 break;
2692 }
2693 }
2694 }
2695done:
2696 if (graph)
2697 got_commit_graph_close(graph);
2698 free(head_commit_id);
2699 if (!err && !is_same_branch)
2700 err = got_error(GOT_ERR_ANCESTRY55);
2701 return err;
2702}
2703
2704static const struct got_error *
2705checkout_ancestry_error(struct got_reference *ref, const char *commit_id_str)
2706{
2707 static char msg[512];
2708 const char *branch_name;
2709
2710 if (got_ref_is_symbolic(ref))
2711 branch_name = got_ref_get_symref_target(ref);
2712 else
2713 branch_name = got_ref_get_name(ref);
2714
2715 if (strncmp("refs/heads/", branch_name, 11) == 0)
2716 branch_name += 11;
2717
2718 snprintf(msg, sizeof(msg),
2719 "target commit is not contained in branch '%s'; "
2720 "the branch to use must be specified with -b; "
2721 "if necessary a new branch can be created for "
2722 "this commit with 'got branch -c %s BRANCH_NAME'",
2723 branch_name, commit_id_str);
2724
2725 return got_error_msg(GOT_ERR_ANCESTRY55, msg);
2726}
2727
2728static const struct got_error *
2729cmd_checkout(int argc, char *argv[])
2730{
2731 const struct got_error *error = NULL((void *)0);
2732 struct got_repository *repo = NULL((void *)0);
2733 struct got_reference *head_ref = NULL((void *)0);
2734 struct got_worktree *worktree = NULL((void *)0);
2735 char *repo_path = NULL((void *)0);
2736 char *worktree_path = NULL((void *)0);
2737 const char *path_prefix = "";
2738 const char *branch_name = GOT_REF_HEAD"HEAD";
2739 char *commit_id_str = NULL((void *)0);
2740 char *cwd = NULL((void *)0);
2741 int ch, same_path_prefix, allow_nonempty = 0;
2742 struct got_pathlist_head paths;
2743 struct got_checkout_progress_arg cpa;
2744
2745 TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)->
tqh_last = &(&paths)->tqh_first; } while (0)
;
2746
2747 while ((ch = getopt(argc, argv, "b:c:Ep:")) != -1) {
2748 switch (ch) {
2749 case 'b':
2750 branch_name = optarg;
2751 break;
2752 case 'c':
2753 commit_id_str = strdup(optarg);
2754 if (commit_id_str == NULL((void *)0))
2755 return got_error_from_errno("strdup");
2756 break;
2757 case 'E':
2758 allow_nonempty = 1;
2759 break;
2760 case 'p':
2761 path_prefix = optarg;
2762 break;
2763 default:
2764 usage_checkout();
2765 /* NOTREACHED */
2766 }
2767 }
2768
2769 argc -= optind;
2770 argv += optind;
2771
2772#ifndef PROFILE
2773 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
2774 "unveil", NULL((void *)0)) == -1)
2775 err(1, "pledge");
2776#endif
2777 if (argc == 1) {
2778 char *base, *dotgit;
2779 const char *path;
2780 repo_path = realpath(argv[0], NULL((void *)0));
2781 if (repo_path == NULL((void *)0))
2782 return got_error_from_errno2("realpath", argv[0]);
2783 cwd = getcwd(NULL((void *)0), 0);
2784 if (cwd == NULL((void *)0)) {
2785 error = got_error_from_errno("getcwd");
2786 goto done;
2787 }
2788 if (path_prefix[0])
2789 path = path_prefix;
2790 else
2791 path = repo_path;
2792 error = got_path_basename(&base, path);
2793 if (error)
2794 goto done;
2795 dotgit = strstr(base, ".git");
2796 if (dotgit)
2797 *dotgit = '\0';
2798 if (asprintf(&worktree_path, "%s/%s", cwd, base) == -1) {
2799 error = got_error_from_errno("asprintf");
2800 free(base);
2801 goto done;
2802 }
2803 free(base);
2804 } else if (argc == 2) {
2805 repo_path = realpath(argv[0], NULL((void *)0));
2806 if (repo_path == NULL((void *)0)) {
2807 error = got_error_from_errno2("realpath", argv[0]);
2808 goto done;
2809 }
2810 worktree_path = realpath(argv[1], NULL((void *)0));
2811 if (worktree_path == NULL((void *)0)) {
2812 if (errno(*__errno()) != ENOENT2) {
2813 error = got_error_from_errno2("realpath",
2814 argv[1]);
2815 goto done;
2816 }
2817 worktree_path = strdup(argv[1]);
2818 if (worktree_path == NULL((void *)0)) {
2819 error = got_error_from_errno("strdup");
2820 goto done;
2821 }
2822 }
2823 } else
2824 usage_checkout();
2825
2826 got_path_strip_trailing_slashes(repo_path);
2827 got_path_strip_trailing_slashes(worktree_path);
2828
2829 error = got_repo_open(&repo, repo_path, NULL((void *)0));
2830 if (error != NULL((void *)0))
2831 goto done;
2832
2833 /* Pre-create work tree path for unveil(2) */
2834 error = got_path_mkdir(worktree_path);
2835 if (error) {
2836 if (!(error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EISDIR21) &&
2837 !(error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EEXIST17))
2838 goto done;
2839 if (!allow_nonempty &&
2840 !got_path_dir_is_empty(worktree_path)) {
2841 error = got_error_path(worktree_path,
2842 GOT_ERR_DIR_NOT_EMPTY75);
2843 goto done;
2844 }
2845 }
2846
2847 error = apply_unveil(got_repo_get_path(repo), 0, worktree_path);
2848 if (error)
2849 goto done;
2850
2851 error = got_ref_open(&head_ref, repo, branch_name, 0);
2852 if (error != NULL((void *)0))
2853 goto done;
2854
2855 error = got_worktree_init(worktree_path, head_ref, path_prefix, repo);
2856 if (error != NULL((void *)0) && !(error->code == GOT_ERR_ERRNO1 && errno(*__errno()) == EEXIST17))
2857 goto done;
2858
2859 error = got_worktree_open(&worktree, worktree_path);
2860 if (error != NULL((void *)0))
2861 goto done;
2862
2863 error = got_worktree_match_path_prefix(&same_path_prefix, worktree,
2864 path_prefix);
2865 if (error != NULL((void *)0))
2866 goto done;
2867 if (!same_path_prefix) {
2868 error = got_error(GOT_ERR_PATH_PREFIX54);
2869 goto done;
2870 }
2871
2872 if (commit_id_str) {
2873 struct got_object_id *commit_id;
2874 struct got_reflist_head refs;
2875 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
2876 error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name,
2877 NULL((void *)0));
2878 if (error)
2879 goto done;
2880 error = got_repo_match_object_id(&commit_id, NULL((void *)0),
2881 commit_id_str, GOT_OBJ_TYPE_COMMIT1, &refs, repo);
2882 got_ref_list_free(&refs);
2883 if (error)
2884 goto done;
2885 error = check_linear_ancestry(commit_id,
2886 got_worktree_get_base_commit_id(worktree), 0, repo);
2887 if (error != NULL((void *)0)) {
2888 free(commit_id);
2889 if (error->code == GOT_ERR_ANCESTRY55) {
2890 error = checkout_ancestry_error(
2891 head_ref, commit_id_str);
2892 }
2893 goto done;
2894 }
2895 error = check_same_branch(commit_id, head_ref, NULL((void *)0), repo);
2896 if (error) {
2897 if (error->code == GOT_ERR_ANCESTRY55) {
2898 error = checkout_ancestry_error(
2899 head_ref, commit_id_str);
2900 }
2901 goto done;
2902 }
2903 error = got_worktree_set_base_commit_id(worktree, repo,
2904 commit_id);
2905 free(commit_id);
2906 if (error)
2907 goto done;
2908 }
2909
2910 error = got_pathlist_append(&paths, "", NULL((void *)0));
2911 if (error)
2912 goto done;
2913 cpa.worktree_path = worktree_path;
2914 cpa.had_base_commit_ref_error = 0;
2915 error = got_worktree_checkout_files(worktree, &paths, repo,
2916 checkout_progress, &cpa, check_cancelled, NULL((void *)0));
2917 if (error != NULL((void *)0))
2918 goto done;
2919
2920 printf("Now shut up and hack\n");
2921 if (cpa.had_base_commit_ref_error)
2922 show_worktree_base_ref_warning();
2923done:
2924 got_pathlist_free(&paths);
2925 free(commit_id_str);
2926 free(repo_path);
2927 free(worktree_path);
2928 free(cwd);
2929 return error;
2930}
2931
2932struct got_update_progress_arg {
2933 int did_something;
2934 int conflicts;
2935 int obstructed;
2936 int not_updated;
2937};
2938
2939void
2940print_update_progress_stats(struct got_update_progress_arg *upa)
2941{
2942 if (!upa->did_something)
2943 return;
2944
2945 if (upa->conflicts > 0)
2946 printf("Files with new merge conflicts: %d\n", upa->conflicts);
2947 if (upa->obstructed > 0)
2948 printf("File paths obstructed by a non-regular file: %d\n",
2949 upa->obstructed);
2950 if (upa->not_updated > 0)
2951 printf("Files not updated because of existing merge "
2952 "conflicts: %d\n", upa->not_updated);
2953}
2954
2955__dead__attribute__((__noreturn__)) static void
2956usage_update(void)
2957{
2958 fprintf(stderr(&__sF[2]), "usage: %s update [-b branch] [-c commit] [path ...]\n",
2959 getprogname());
2960 exit(1);
2961}
2962
2963static const struct got_error *
2964update_progress(void *arg, unsigned char status, const char *path)
2965{
2966 struct got_update_progress_arg *upa = arg;
2967
2968 if (status == GOT_STATUS_EXISTS'E' ||
2969 status == GOT_STATUS_BASE_REF_ERR'B')
2970 return NULL((void *)0);
2971
2972 upa->did_something = 1;
2973
2974 /* Base commit bump happens silently. */
2975 if (status == GOT_STATUS_BUMP_BASE'b')
2976 return NULL((void *)0);
2977
2978 if (status == GOT_STATUS_CONFLICT'C')
2979 upa->conflicts++;
2980 if (status == GOT_STATUS_OBSTRUCTED'~')
2981 upa->obstructed++;
2982 if (status == GOT_STATUS_CANNOT_UPDATE'#')
2983 upa->not_updated++;
2984
2985 while (path[0] == '/')
2986 path++;
2987 printf("%c %s\n", status, path);
2988 return NULL((void *)0);
2989}
2990
2991static const struct got_error *
2992switch_head_ref(struct got_reference *head_ref,
2993 struct got_object_id *commit_id, struct got_worktree *worktree,
2994 struct got_repository *repo)
2995{
2996 const struct got_error *err = NULL((void *)0);
2997 char *base_id_str;
2998 int ref_has_moved = 0;
2999
3000 /* Trivial case: switching between two different references. */
3001 if (strcmp(got_ref_get_name(head_ref),
3002 got_worktree_get_head_ref_name(worktree)) != 0) {
3003 printf("Switching work tree from %s to %s\n",
3004 got_worktree_get_head_ref_name(worktree),
3005 got_ref_get_name(head_ref));
3006 return got_worktree_set_head_ref(worktree, head_ref);
3007 }
3008
3009 err = check_linear_ancestry(commit_id,
3010 got_worktree_get_base_commit_id(worktree), 0, repo);
3011 if (err) {
3012 if (err->code != GOT_ERR_ANCESTRY55)
3013 return err;
3014 ref_has_moved = 1;
3015 }
3016 if (!ref_has_moved)
3017 return NULL((void *)0);
3018
3019 /* Switching to a rebased branch with the same reference name. */
3020 err = got_object_id_str(&base_id_str,
3021 got_worktree_get_base_commit_id(worktree));
3022 if (err)
3023 return err;
3024 printf("Reference %s now points at a different branch\n",
3025 got_worktree_get_head_ref_name(worktree));
3026 printf("Switching work tree from %s to %s\n", base_id_str,
3027 got_worktree_get_head_ref_name(worktree));
3028 return NULL((void *)0);
3029}
3030
3031static const struct got_error *
3032check_rebase_or_histedit_in_progress(struct got_worktree *worktree)
3033{
3034 const struct got_error *err;
3035 int in_progress;
3036
3037 err = got_worktree_rebase_in_progress(&in_progress, worktree);
3038 if (err)
3039 return err;
3040 if (in_progress)
3041 return got_error(GOT_ERR_REBASING88);
3042
3043 err = got_worktree_histedit_in_progress(&in_progress, worktree);
3044 if (err)
3045 return err;
3046 if (in_progress)
3047 return got_error(GOT_ERR_HISTEDIT_BUSY96);
3048
3049 return NULL((void *)0);
3050}
3051
3052static const struct got_error *
3053get_worktree_paths_from_argv(struct got_pathlist_head *paths, int argc,
3054 char *argv[], struct got_worktree *worktree)
3055{
3056 const struct got_error *err = NULL((void *)0);
3057 char *path;
3058 int i;
3059
3060 if (argc == 0) {
3061 path = strdup("");
3062 if (path == NULL((void *)0))
3063 return got_error_from_errno("strdup");
3064 return got_pathlist_append(paths, path, NULL((void *)0));
3065 }
3066
3067 for (i = 0; i < argc; i++) {
3068 err = got_worktree_resolve_path(&path, worktree, argv[i]);
3069 if (err)
3070 break;
3071 err = got_pathlist_append(paths, path, NULL((void *)0));
3072 if (err) {
3073 free(path);
3074 break;
3075 }
3076 }
3077
3078 return err;
3079}
3080
3081static const struct got_error *
3082wrap_not_worktree_error(const struct got_error *orig_err,
3083 const char *cmdname, const char *path)
3084{
3085 const struct got_error *err;
3086 struct got_repository *repo;
3087 static char msg[512];
3088
3089 err = got_repo_open(&repo, path, NULL((void *)0));
3090 if (err)
3091 return orig_err;
3092
3093 snprintf(msg, sizeof(msg),
3094 "'got %s' needs a work tree in addition to a git repository\n"
3095 "Work trees can be checked out from this Git repository with "
3096 "'got checkout'.\n"
3097 "The got(1) manual page contains more information.", cmdname);
3098 err = got_error_msg(GOT_ERR_NOT_WORKTREE60, msg);
3099 got_repo_close(repo);
3100 return err;
3101}
3102
3103static const struct got_error *
3104cmd_update(int argc, char *argv[])
3105{
3106 const struct got_error *error = NULL((void *)0);
3107 struct got_repository *repo = NULL((void *)0);
3108 struct got_worktree *worktree = NULL((void *)0);
3109 char *worktree_path = NULL((void *)0);
3110 struct got_object_id *commit_id = NULL((void *)0);
3111 char *commit_id_str = NULL((void *)0);
3112 const char *branch_name = NULL((void *)0);
3113 struct got_reference *head_ref = NULL((void *)0);
3114 struct got_pathlist_head paths;
3115 struct got_pathlist_entry *pe;
3116 int ch;
3117 struct got_update_progress_arg upa;
3118
3119 TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)->
tqh_last = &(&paths)->tqh_first; } while (0)
;
3120
3121 while ((ch = getopt(argc, argv, "b:c:")) != -1) {
3122 switch (ch) {
3123 case 'b':
3124 branch_name = optarg;
3125 break;
3126 case 'c':
3127 commit_id_str = strdup(optarg);
3128 if (commit_id_str == NULL((void *)0))
3129 return got_error_from_errno("strdup");
3130 break;
3131 default:
3132 usage_update();
3133 /* NOTREACHED */
3134 }
3135 }
3136
3137 argc -= optind;
3138 argv += optind;
3139
3140#ifndef PROFILE
3141 if (pledge("stdio rpath wpath cpath fattr flock proc exec sendfd "
3142 "unveil", NULL((void *)0)) == -1)
3143 err(1, "pledge");
3144#endif
3145 worktree_path = getcwd(NULL((void *)0), 0);
3146 if (worktree_path == NULL((void *)0)) {
3147 error = got_error_from_errno("getcwd");
3148 goto done;
3149 }
3150 error = got_worktree_open(&worktree, worktree_path);
3151 if (error) {
3152 if (error->code == GOT_ERR_NOT_WORKTREE60)
3153 error = wrap_not_worktree_error(error, "update",
3154 worktree_path);
3155 goto done;
3156 }
3157
3158 error = check_rebase_or_histedit_in_progress(worktree);
3159 if (error)
3160 goto done;
3161
3162 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
3163 NULL((void *)0));
3164 if (error != NULL((void *)0))
3165 goto done;
3166
3167 error = apply_unveil(got_repo_get_path(repo), 0,
3168 got_worktree_get_root_path(worktree));
3169 if (error)
3170 goto done;
3171
3172 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
3173 if (error)
3174 goto done;
3175
3176 error = got_ref_open(&head_ref, repo, branch_name ? branch_name :
3177 got_worktree_get_head_ref_name(worktree), 0);
3178 if (error != NULL((void *)0))
3179 goto done;
3180 if (commit_id_str == NULL((void *)0)) {
3181 error = got_ref_resolve(&commit_id, repo, head_ref);
3182 if (error != NULL((void *)0))
3183 goto done;
3184 error = got_object_id_str(&commit_id_str, commit_id);
3185 if (error != NULL((void *)0))
3186 goto done;
3187 } else {
3188 struct got_reflist_head refs;
3189 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
3190 error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name,
3191 NULL((void *)0));
3192 if (error)
3193 goto done;
3194 error = got_repo_match_object_id(&commit_id, NULL((void *)0),
3195 commit_id_str, GOT_OBJ_TYPE_COMMIT1, &refs, repo);
3196 got_ref_list_free(&refs);
3197 free(commit_id_str);
3198 commit_id_str = NULL((void *)0);
3199 if (error)
3200 goto done;
3201 error = got_object_id_str(&commit_id_str, commit_id);
3202 if (error)
3203 goto done;
3204 }
3205
3206 if (branch_name) {
3207 struct got_object_id *head_commit_id;
3208 TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0);
(pe) = ((pe)->entry.tqe_next))
{
3209 if (pe->path_len == 0)
3210 continue;
3211 error = got_error_msg(GOT_ERR_BAD_PATH4,
3212 "switching between branches requires that "
3213 "the entire work tree gets updated");
3214 goto done;
3215 }
3216 error = got_ref_resolve(&head_commit_id, repo, head_ref);
3217 if (error)
3218 goto done;
3219 error = check_linear_ancestry(commit_id, head_commit_id, 0,
3220 repo);
3221 free(head_commit_id);
3222 if (error != NULL((void *)0))
3223 goto done;
3224 error = check_same_branch(commit_id, head_ref, NULL((void *)0), repo);
3225 if (error)
3226 goto done;
3227 error = switch_head_ref(head_ref, commit_id, worktree, repo);
3228 if (error)
3229 goto done;
3230 } else {
3231 error = check_linear_ancestry(commit_id,
3232 got_worktree_get_base_commit_id(worktree), 0, repo);
3233 if (error != NULL((void *)0)) {
3234 if (error->code == GOT_ERR_ANCESTRY55)
3235 error = got_error(GOT_ERR_BRANCH_MOVED77);
3236 goto done;
3237 }
3238 error = check_same_branch(commit_id, head_ref, NULL((void *)0), repo);
3239 if (error)
3240 goto done;
3241 }
3242
3243 if (got_object_id_cmp(got_worktree_get_base_commit_id(worktree),
3244 commit_id) != 0) {
3245 error = got_worktree_set_base_commit_id(worktree, repo,
3246 commit_id);
3247 if (error)
3248 goto done;
3249 }
3250
3251 memset(&upa, 0, sizeof(upa));
3252 error = got_worktree_checkout_files(worktree, &paths, repo,
3253 update_progress, &upa, check_cancelled, NULL((void *)0));
3254 if (error != NULL((void *)0))
3255 goto done;
3256
3257 if (upa.did_something)
3258 printf("Updated to commit %s\n", commit_id_str);
3259 else
3260 printf("Already up-to-date\n");
3261 print_update_progress_stats(&upa);
3262done:
3263 free(worktree_path);
3264 TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0);
(pe) = ((pe)->entry.tqe_next))
3265 free((char *)pe->path);
3266 got_pathlist_free(&paths);
3267 free(commit_id);
3268 free(commit_id_str);
3269 return error;
3270}
3271
3272static const struct got_error *
3273diff_blobs(struct got_object_id *blob_id1, struct got_object_id *blob_id2,
3274 const char *path, int diff_context, int ignore_whitespace,
3275 int force_text_diff, struct got_repository *repo)
3276{
3277 const struct got_error *err = NULL((void *)0);
3278 struct got_blob_object *blob1 = NULL((void *)0), *blob2 = NULL((void *)0);
3279
3280 if (blob_id1) {
3281 err = got_object_open_as_blob(&blob1, repo, blob_id1, 8192);
3282 if (err)
3283 goto done;
3284 }
3285
3286 err = got_object_open_as_blob(&blob2, repo, blob_id2, 8192);
3287 if (err)
3288 goto done;
3289
3290 while (path[0] == '/')
3291 path++;
3292 err = got_diff_blob(NULL((void *)0), NULL((void *)0), blob1, blob2, path, path,
3293 diff_context, ignore_whitespace, force_text_diff, stdout(&__sF[1]));
3294done:
3295 if (blob1)
3296 got_object_blob_close(blob1);
3297 got_object_blob_close(blob2);
3298 return err;
3299}
3300
3301static const struct got_error *
3302diff_trees(struct got_object_id *tree_id1, struct got_object_id *tree_id2,
3303 const char *path, int diff_context, int ignore_whitespace,
3304 int force_text_diff, struct got_repository *repo)
3305{
3306 const struct got_error *err = NULL((void *)0);
3307 struct got_tree_object *tree1 = NULL((void *)0), *tree2 = NULL((void *)0);
3308 struct got_diff_blob_output_unidiff_arg arg;
3309
3310 if (tree_id1) {
3311 err = got_object_open_as_tree(&tree1, repo, tree_id1);
3312 if (err)
3313 goto done;
3314 }
3315
3316 err = got_object_open_as_tree(&tree2, repo, tree_id2);
3317 if (err)
3318 goto done;
3319
3320 arg.diff_context = diff_context;
3321 arg.ignore_whitespace = ignore_whitespace;
3322 arg.force_text_diff = force_text_diff;
3323 arg.outfile = stdout(&__sF[1]);
3324 arg.line_offsets = NULL((void *)0);
3325 arg.nlines = 0;
3326 while (path[0] == '/')
3327 path++;
3328 err = got_diff_tree(tree1, tree2, path, path, repo,
3329 got_diff_blob_output_unidiff, &arg, 1);
3330done:
3331 if (tree1)
3332 got_object_tree_close(tree1);
3333 if (tree2)
3334 got_object_tree_close(tree2);
3335 return err;
3336}
3337
3338static const struct got_error *
3339get_changed_paths(struct got_pathlist_head *paths,
3340 struct got_commit_object *commit, struct got_repository *repo)
3341{
3342 const struct got_error *err = NULL((void *)0);
3343 struct got_object_id *tree_id1 = NULL((void *)0), *tree_id2 = NULL((void *)0);
3344 struct got_tree_object *tree1 = NULL((void *)0), *tree2 = NULL((void *)0);
3345 struct got_object_qid *qid;
3346
3347 qid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit))((got_object_commit_get_parent_ids(commit))->sqh_first);
3348 if (qid != NULL((void *)0)) {
3349 struct got_commit_object *pcommit;
3350 err = got_object_open_as_commit(&pcommit, repo,
3351 qid->id);
3352 if (err)
3353 return err;
3354
3355 tree_id1 = got_object_commit_get_tree_id(pcommit);
3356 got_object_commit_close(pcommit);
3357
3358 }
3359
3360 if (tree_id1) {
3361 err = got_object_open_as_tree(&tree1, repo, tree_id1);
3362 if (err)
3363 goto done;
3364 }
3365
3366 tree_id2 = got_object_commit_get_tree_id(commit);
3367 err = got_object_open_as_tree(&tree2, repo, tree_id2);
3368 if (err)
3369 goto done;
3370
3371 err = got_diff_tree(tree1, tree2, "", "", repo,
3372 got_diff_tree_collect_changed_paths, paths, 0);
3373done:
3374 if (tree1)
3375 got_object_tree_close(tree1);
3376 if (tree2)
3377 got_object_tree_close(tree2);
3378 return err;
3379}
3380
3381static const struct got_error *
3382print_patch(struct got_commit_object *commit, struct got_object_id *id,
3383 const char *path, int diff_context, struct got_repository *repo)
3384{
3385 const struct got_error *err = NULL((void *)0);
3386 struct got_commit_object *pcommit = NULL((void *)0);
3387 char *id_str1 = NULL((void *)0), *id_str2 = NULL((void *)0);
3388 struct got_object_id *obj_id1 = NULL((void *)0), *obj_id2 = NULL((void *)0);
3389 struct got_object_qid *qid;
3390
3391 qid = SIMPLEQ_FIRST(got_object_commit_get_parent_ids(commit))((got_object_commit_get_parent_ids(commit))->sqh_first);
3392 if (qid != NULL((void *)0)) {
3393 err = got_object_open_as_commit(&pcommit, repo,
3394 qid->id);
3395 if (err)
3396 return err;
3397 }
3398
3399 if (path && path[0] != '\0') {
3400 int obj_type;
3401 err = got_object_id_by_path(&obj_id2, repo, id, path);
3402 if (err)
3403 goto done;
3404 err = got_object_id_str(&id_str2, obj_id2);
3405 if (err) {
3406 free(obj_id2);
3407 goto done;
3408 }
3409 if (pcommit) {
3410 err = got_object_id_by_path(&obj_id1, repo,
3411 qid->id, path);
3412 if (err) {
3413 if (err->code != GOT_ERR_NO_TREE_ENTRY50) {
3414 free(obj_id2);
3415 goto done;
3416 }
3417 } else {
3418 err = got_object_id_str(&id_str1, obj_id1);
3419 if (err) {
3420 free(obj_id2);
3421 goto done;
3422 }
3423 }
3424 }
3425 err = got_object_get_type(&obj_type, repo, obj_id2);
3426 if (err) {
3427 free(obj_id2);
3428 goto done;
3429 }
3430 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null", id_str2);
3431 switch (obj_type) {
3432 case GOT_OBJ_TYPE_BLOB3:
3433 err = diff_blobs(obj_id1, obj_id2, path, diff_context,
3434 0, 0, repo);
3435 break;
3436 case GOT_OBJ_TYPE_TREE2:
3437 err = diff_trees(obj_id1, obj_id2, path, diff_context,
3438 0, 0, repo);
3439 break;
3440 default:
3441 err = got_error(GOT_ERR_OBJ_TYPE11);
3442 break;
3443 }
3444 free(obj_id1);
3445 free(obj_id2);
3446 } else {
3447 obj_id2 = got_object_commit_get_tree_id(commit);
3448 err = got_object_id_str(&id_str2, obj_id2);
3449 if (err)
3450 goto done;
3451 if (pcommit) {
3452 obj_id1 = got_object_commit_get_tree_id(pcommit);
3453 err = got_object_id_str(&id_str1, obj_id1);
3454 if (err)
3455 goto done;
3456 }
3457 printf("diff %s %s\n", id_str1 ? id_str1 : "/dev/null",
3458 id_str2);
3459 err = diff_trees(obj_id1, obj_id2, "", diff_context, 0, 0,
3460 repo);
3461 }
3462done:
3463 free(id_str1);
3464 free(id_str2);
3465 if (pcommit)
3466 got_object_commit_close(pcommit);
3467 return err;
3468}
3469
3470static char *
3471get_datestr(time_t *time, char *datebuf)
3472{
3473 struct tm mytm, *tm;
3474 char *p, *s;
3475
3476 tm = gmtime_r(time, &mytm);
3477 if (tm == NULL((void *)0))
3478 return NULL((void *)0);
3479 s = asctime_r(tm, datebuf);
3480 if (s == NULL((void *)0))
3481 return NULL((void *)0);
3482 p = strchr(s, '\n');
3483 if (p)
3484 *p = '\0';
3485 return s;
3486}
3487
3488static const struct got_error *
3489match_logmsg(int *have_match, struct got_object_id *id,
3490 struct got_commit_object *commit, regex_t *regex)
3491{
3492 const struct got_error *err = NULL((void *)0);
3493 regmatch_t regmatch;
3494 char *id_str = NULL((void *)0), *logmsg = NULL((void *)0);
3495
3496 *have_match = 0;
3497
3498 err = got_object_id_str(&id_str, id);
3499 if (err)
3500 return err;
3501
3502 err = got_object_commit_get_logmsg(&logmsg, commit);
3503 if (err)
3504 goto done;
3505
3506 if (regexec(regex, logmsg, 1, &regmatch, 0) == 0)
3507 *have_match = 1;
3508done:
3509 free(id_str);
3510 free(logmsg);
3511 return err;
3512}
3513
3514static void
3515match_changed_paths(int *have_match, struct got_pathlist_head *changed_paths,
3516 regex_t *regex)
3517{
3518 regmatch_t regmatch;
3519 struct got_pathlist_entry *pe;
3520
3521 *have_match = 0;
3522
3523 TAILQ_FOREACH(pe, changed_paths, entry)for((pe) = ((changed_paths)->tqh_first); (pe) != ((void *)
0); (pe) = ((pe)->entry.tqe_next))
{
3524 if (regexec(regex, pe->path, 1, &regmatch, 0) == 0) {
3525 *have_match = 1;
3526 break;
3527 }
3528 }
3529}
3530
3531#define GOT_COMMIT_SEP_STR"-----------------------------------------------\n" "-----------------------------------------------\n"
3532
3533static const struct got_error*
3534build_refs_str(char **refs_str, struct got_reflist_head *refs,
3535 struct got_object_id *id, struct got_repository *repo)
3536{
3537 static const struct got_error *err = NULL((void *)0);
3538 struct got_reflist_entry *re;
3539 char *s;
3540 const char *name;
3541
3542 *refs_str = NULL((void *)0);
3543
3544 TAILQ_FOREACH(re, refs, entry)for((re) = ((refs)->tqh_first); (re) != ((void *)0); (re) =
((re)->entry.tqe_next))
{
3545 struct got_tag_object *tag = NULL((void *)0);
3546 struct got_object_id *ref_id;
3547 int cmp;
3548
3549 name = got_ref_get_name(re->ref);
3550 if (strcmp(name, GOT_REF_HEAD"HEAD") == 0)
3551 continue;
3552 if (strncmp(name, "refs/", 5) == 0)
3553 name += 5;
3554 if (strncmp(name, "got/", 4) == 0)
3555 continue;
3556 if (strncmp(name, "heads/", 6) == 0)
3557 name += 6;
3558 if (strncmp(name, "remotes/", 8) == 0) {
3559 name += 8;
3560 s = strstr(name, "/" GOT_REF_HEAD"HEAD");
3561 if (s != NULL((void *)0) && s[strlen(s)] == '\0')
3562 continue;
3563 }
3564 err = got_ref_resolve(&ref_id, repo, re->ref);
3565 if (err)
3566 break;
3567 if (strncmp(name, "tags/", 5) == 0) {
3568 err = got_object_open_as_tag(&tag, repo, ref_id);
3569 if (err) {
3570 if (err->code != GOT_ERR_OBJ_TYPE11) {
3571 free(ref_id);
3572 break;
3573 }
3574 /* Ref points at something other than a tag. */
3575 err = NULL((void *)0);
3576 tag = NULL((void *)0);
3577 }
3578 }
3579 cmp = got_object_id_cmp(tag ?
3580 got_object_tag_get_object_id(tag) : ref_id, id);
3581 free(ref_id);
3582 if (tag)
3583 got_object_tag_close(tag);
3584 if (cmp != 0)
3585 continue;
3586 s = *refs_str;
3587 if (asprintf(refs_str, "%s%s%s", s ? s : "",
3588 s ? ", " : "", name) == -1) {
3589 err = got_error_from_errno("asprintf");
3590 free(s);
3591 *refs_str = NULL((void *)0);
3592 break;
3593 }
3594 free(s);
3595 }
3596
3597 return err;
3598}
3599
3600static const struct got_error *
3601print_commit(struct got_commit_object *commit, struct got_object_id *id,
3602 struct got_repository *repo, const char *path,
3603 struct got_pathlist_head *changed_paths, int show_patch,
3604 int diff_context, struct got_reflist_object_id_map *refs_idmap,
3605 const char *custom_refs_str)
3606{
3607 const struct got_error *err = NULL((void *)0);
3608 char *id_str, *datestr, *logmsg0, *logmsg, *line;
3609 char datebuf[26];
3610 time_t committer_time;
3611 const char *author, *committer;
3612 char *refs_str = NULL((void *)0);
3613
3614 err = got_object_id_str(&id_str, id);
3615 if (err)
3616 return err;
3617
3618 if (custom_refs_str == NULL((void *)0)) {
3619 struct got_reflist_head *refs;
3620 refs = got_reflist_object_id_map_lookup(refs_idmap, id);
3621 if (refs) {
3622 err = build_refs_str(&refs_str, refs, id, repo);
3623 if (err)
3624 goto done;
3625 }
3626 }
3627
3628 printf(GOT_COMMIT_SEP_STR"-----------------------------------------------\n");
3629 if (custom_refs_str)
3630 printf("commit %s (%s)\n", id_str, custom_refs_str);
3631 else
3632 printf("commit %s%s%s%s\n", id_str, refs_str ? " (" : "",
3633 refs_str ? refs_str : "", refs_str ? ")" : "");
3634 free(id_str);
3635 id_str = NULL((void *)0);
3636 free(refs_str);
3637 refs_str = NULL((void *)0);
3638 printf("from: %s\n", got_object_commit_get_author(commit));
3639 committer_time = got_object_commit_get_committer_time(commit);
3640 datestr = get_datestr(&committer_time, datebuf);
3641 if (datestr)
3642 printf("date: %s UTC\n", datestr);
3643 author = got_object_commit_get_author(commit);
3644 committer = got_object_commit_get_committer(commit);
3645 if (strcmp(author, committer) != 0)
3646 printf("via: %s\n", committer);
3647 if (got_object_commit_get_nparents(commit) > 1) {
3648 const struct got_object_id_queue *parent_ids;
3649 struct got_object_qid *qid;
3650 int n = 1;
3651 parent_ids = got_object_commit_get_parent_ids(commit);
3652 SIMPLEQ_FOREACH(qid, parent_ids, entry)for((qid) = ((parent_ids)->sqh_first); (qid) != ((void *)0
); (qid) = ((qid)->entry.sqe_next))
{
3653 err = got_object_id_str(&id_str, qid->id);
3654 if (err)
3655 goto done;
3656 printf("parent %d: %s\n", n++, id_str);
3657 free(id_str);
3658 id_str = NULL((void *)0);
3659 }
3660 }
3661
3662 err = got_object_commit_get_logmsg(&logmsg0, commit);
3663 if (err)
3664 goto done;
3665
3666 logmsg = logmsg0;
3667 do {
3668 line = strsep(&logmsg, "\n");
3669 if (line)
3670 printf(" %s\n", line);
3671 } while (line);
3672 free(logmsg0);
3673
3674 if (changed_paths) {
3675 struct got_pathlist_entry *pe;
3676 TAILQ_FOREACH(pe, changed_paths, entry)for((pe) = ((changed_paths)->tqh_first); (pe) != ((void *)
0); (pe) = ((pe)->entry.tqe_next))
{
3677 struct got_diff_changed_path *cp = pe->data;
3678 printf(" %c %s\n", cp->status, pe->path);
3679 }
3680 printf("\n");
3681 }
3682 if (show_patch) {
3683 err = print_patch(commit, id, path, diff_context, repo);
3684 if (err == 0)
3685 printf("\n");
3686 }
3687
3688 if (fflush(stdout(&__sF[1])) != 0 && err == NULL((void *)0))
3689 err = got_error_from_errno("fflush");
3690done:
3691 free(id_str);
3692 free(refs_str);
3693 return err;
3694}
3695
3696static const struct got_error *
3697print_commits(struct got_object_id *root_id, struct got_object_id *end_id,
3698 struct got_repository *repo, const char *path, int show_changed_paths,
3699 int show_patch, const char *search_pattern, int diff_context, int limit,
3700 int log_branches, int reverse_display_order,
3701 struct got_reflist_object_id_map *refs_idmap)
3702{
3703 const struct got_error *err;
3704 struct got_commit_graph *graph;
3705 regex_t regex;
3706 int have_match;
3707 struct got_object_id_queue reversed_commits;
3708 struct got_object_qid *qid;
3709 struct got_commit_object *commit;
3710 struct got_pathlist_head changed_paths;
3711 struct got_pathlist_entry *pe;
3712
3713 SIMPLEQ_INIT(&reversed_commits)do { (&reversed_commits)->sqh_first = ((void *)0); (&
reversed_commits)->sqh_last = &(&reversed_commits)
->sqh_first; } while (0)
;
3714 TAILQ_INIT(&changed_paths)do { (&changed_paths)->tqh_first = ((void *)0); (&
changed_paths)->tqh_last = &(&changed_paths)->tqh_first
; } while (0)
;
3715
3716 if (search_pattern && regcomp(&regex, search_pattern,
3717 REG_EXTENDED0001 | REG_NOSUB0004 | REG_NEWLINE0010))
3718 return got_error_msg(GOT_ERR_REGEX112, search_pattern);
3719
3720 err = got_commit_graph_open(&graph, path, !log_branches);
3721 if (err)
3722 return err;
3723 err = got_commit_graph_iter_start(graph, root_id, repo,
3724 check_cancelled, NULL((void *)0));
3725 if (err)
3726 goto done;
3727 for (;;) {
3728 struct got_object_id *id;
3729
3730 if (sigint_received || sigpipe_received)
3731 break;
3732
3733 err = got_commit_graph_iter_next(&id, graph, repo,
3734 check_cancelled, NULL((void *)0));
3735 if (err) {
3736 if (err->code == GOT_ERR_ITER_COMPLETED46)
3737 err = NULL((void *)0);
3738 break;
3739 }
3740 if (id == NULL((void *)0))
3741 break;
3742
3743 err = got_object_open_as_commit(&commit, repo, id);
3744 if (err)
3745 break;
3746
3747 if (show_changed_paths && !reverse_display_order) {
3748 err = get_changed_paths(&changed_paths, commit, repo);
3749 if (err)
3750 break;
3751 }
3752
3753 if (search_pattern) {
3754 err = match_logmsg(&have_match, id, commit, &regex);
3755 if (err) {
3756 got_object_commit_close(commit);
3757 break;
3758 }
3759 if (have_match == 0 && show_changed_paths)
3760 match_changed_paths(&have_match,
3761 &changed_paths, &regex);
3762 if (have_match == 0) {
3763 got_object_commit_close(commit);
3764 TAILQ_FOREACH(pe, &changed_paths, entry)for((pe) = ((&changed_paths)->tqh_first); (pe) != ((void
*)0); (pe) = ((pe)->entry.tqe_next))
{
3765 free((char *)pe->path);
3766 free(pe->data);
3767 }
3768 got_pathlist_free(&changed_paths);
3769 continue;
3770 }
3771 }
3772
3773 if (reverse_display_order) {
3774 err = got_object_qid_alloc(&qid, id);
3775 if (err)
3776 break;
3777 SIMPLEQ_INSERT_HEAD(&reversed_commits, qid, entry)do { if (((qid)->entry.sqe_next = (&reversed_commits)->
sqh_first) == ((void *)0)) (&reversed_commits)->sqh_last
= &(qid)->entry.sqe_next; (&reversed_commits)->
sqh_first = (qid); } while (0)
;
3778 got_object_commit_close(commit);
3779 } else {
3780 err = print_commit(commit, id, repo, path,
3781 show_changed_paths ? &changed_paths : NULL((void *)0),
3782 show_patch, diff_context, refs_idmap, NULL((void *)0));
3783 got_object_commit_close(commit);
3784 if (err)
3785 break;
3786 }
3787 if ((limit && --limit == 0) ||
3788 (end_id && got_object_id_cmp(id, end_id) == 0))
3789 break;
3790
3791 TAILQ_FOREACH(pe, &changed_paths, entry)for((pe) = ((&changed_paths)->tqh_first); (pe) != ((void
*)0); (pe) = ((pe)->entry.tqe_next))
{
3792 free((char *)pe->path);
3793 free(pe->data);
3794 }
3795 got_pathlist_free(&changed_paths);
3796 }
3797 if (reverse_display_order) {
3798 SIMPLEQ_FOREACH(qid, &reversed_commits, entry)for((qid) = ((&reversed_commits)->sqh_first); (qid) !=
((void *)0); (qid) = ((qid)->entry.sqe_next))
{
3799 err = got_object_open_as_commit(&commit, repo, qid->id);
3800 if (err)
3801 break;
3802 if (show_changed_paths) {
3803 err = get_changed_paths(&changed_paths,
3804 commit, repo);
3805 if (err)
3806 break;
3807 }
3808 err = print_commit(commit, qid->id, repo, path,
3809 show_changed_paths ? &changed_paths : NULL((void *)0),
3810 show_patch, diff_context, refs_idmap, NULL((void *)0));
3811 got_object_commit_close(commit);
3812 if (err)
3813 break;
3814 TAILQ_FOREACH(pe, &changed_paths, entry)for((pe) = ((&changed_paths)->tqh_first); (pe) != ((void
*)0); (pe) = ((pe)->entry.tqe_next))
{
3815 free((char *)pe->path);
3816 free(pe->data);
3817 }
3818 got_pathlist_free(&changed_paths);
3819 }
3820 }
3821done:
3822 while (!SIMPLEQ_EMPTY(&reversed_commits)(((&reversed_commits)->sqh_first) == ((void *)0))) {
3823 qid = SIMPLEQ_FIRST(&reversed_commits)((&reversed_commits)->sqh_first);
3824 SIMPLEQ_REMOVE_HEAD(&reversed_commits, entry)do { if (((&reversed_commits)->sqh_first = (&reversed_commits
)->sqh_first->entry.sqe_next) == ((void *)0)) (&reversed_commits
)->sqh_last = &(&reversed_commits)->sqh_first; }
while (0)
;
3825 got_object_qid_free(qid);
3826 }
3827 TAILQ_FOREACH(pe, &changed_paths, entry)for((pe) = ((&changed_paths)->tqh_first); (pe) != ((void
*)0); (pe) = ((pe)->entry.tqe_next))
{
3828 free((char *)pe->path);
3829 free(pe->data);
3830 }
3831 got_pathlist_free(&changed_paths);
3832 if (search_pattern)
3833 regfree(&regex);
3834 got_commit_graph_close(graph);
3835 return err;
3836}
3837
3838__dead__attribute__((__noreturn__)) static void
3839usage_log(void)
3840{
3841 fprintf(stderr(&__sF[2]), "usage: %s log [-b] [-c commit] [-C number] [ -l N ] "
3842 "[-p] [-P] [-x commit] [-s search-pattern] [-r repository-path] "
3843 "[-R] [path]\n", getprogname());
3844 exit(1);
3845}
3846
3847static int
3848get_default_log_limit(void)
3849{
3850 const char *got_default_log_limit;
3851 long long n;
3852 const char *errstr;
3853
3854 got_default_log_limit = getenv("GOT_LOG_DEFAULT_LIMIT");
3855 if (got_default_log_limit == NULL((void *)0))
3856 return 0;
3857 n = strtonum(got_default_log_limit, 0, INT_MAX2147483647, &errstr);
3858 if (errstr != NULL((void *)0))
3859 return 0;
3860 return n;
3861}
3862
3863static const struct got_error *
3864cmd_log(int argc, char *argv[])
3865{
3866 const struct got_error *error;
3867 struct got_repository *repo = NULL((void *)0);
3868 struct got_worktree *worktree = NULL((void *)0);
3869 struct got_object_id *start_id = NULL((void *)0), *end_id = NULL((void *)0);
3870 char *repo_path = NULL((void *)0), *path = NULL((void *)0), *cwd = NULL((void *)0), *in_repo_path = NULL((void *)0);
3871 const char *start_commit = NULL((void *)0), *end_commit = NULL((void *)0);
3872 const char *search_pattern = NULL((void *)0);
3873 int diff_context = -1, ch;
3874 int show_changed_paths = 0, show_patch = 0, limit = 0, log_branches = 0;
3875 int reverse_display_order = 0;
3876 const char *errstr;
3877 struct got_reflist_head refs;
3878 struct got_reflist_object_id_map *refs_idmap = NULL((void *)0);
3879
3880 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
3881
3882#ifndef PROFILE
3883 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
3884 NULL((void *)0))
3885 == -1)
3886 err(1, "pledge");
3887#endif
3888
3889 limit = get_default_log_limit();
3890
3891 while ((ch = getopt(argc, argv, "bpPc:C:l:r:Rs:x:")) != -1) {
3892 switch (ch) {
3893 case 'p':
3894 show_patch = 1;
3895 break;
3896 case 'P':
3897 show_changed_paths = 1;
3898 break;
3899 case 'c':
3900 start_commit = optarg;
3901 break;
3902 case 'C':
3903 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT64,
3904 &errstr);
3905 if (errstr != NULL((void *)0))
3906 err(1, "-C option %s", errstr);
3907 break;
3908 case 'l':
3909 limit = strtonum(optarg, 0, INT_MAX2147483647, &errstr);
3910 if (errstr != NULL((void *)0))
3911 err(1, "-l option %s", errstr);
3912 break;
3913 case 'b':
3914 log_branches = 1;
3915 break;
3916 case 'r':
3917 repo_path = realpath(optarg, NULL((void *)0));
3918 if (repo_path == NULL((void *)0))
3919 return got_error_from_errno2("realpath",
3920 optarg);
3921 got_path_strip_trailing_slashes(repo_path);
3922 break;
3923 case 'R':
3924 reverse_display_order = 1;
3925 break;
3926 case 's':
3927 search_pattern = optarg;
3928 break;
3929 case 'x':
3930 end_commit = optarg;
3931 break;
3932 default:
3933 usage_log();
3934 /* NOTREACHED */
3935 }
3936 }
3937
3938 argc -= optind;
3939 argv += optind;
3940
3941 if (diff_context == -1)
3942 diff_context = 3;
3943 else if (!show_patch)
3944 errx(1, "-C requires -p");
3945
3946 cwd = getcwd(NULL((void *)0), 0);
3947 if (cwd == NULL((void *)0)) {
3948 error = got_error_from_errno("getcwd");
3949 goto done;
3950 }
3951
3952 if (repo_path == NULL((void *)0)) {
3953 error = got_worktree_open(&worktree, cwd);
3954 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
3955 goto done;
3956 error = NULL((void *)0);
3957 }
3958
3959 if (argc == 1) {
3960 if (worktree) {
3961 error = got_worktree_resolve_path(&path, worktree,
3962 argv[0]);
3963 if (error)
3964 goto done;
3965 } else {
3966 path = strdup(argv[0]);
3967 if (path == NULL((void *)0)) {
3968 error = got_error_from_errno("strdup");
3969 goto done;
3970 }
3971 }
3972 } else if (argc != 0)
3973 usage_log();
3974
3975 if (repo_path == NULL((void *)0)) {
3976 repo_path = worktree ?
3977 strdup(got_worktree_get_repo_path(worktree)) : strdup(cwd);
3978 }
3979 if (repo_path == NULL((void *)0)) {
3980 error = got_error_from_errno("strdup");
3981 goto done;
3982 }
3983
3984 error = got_repo_open(&repo, repo_path, NULL((void *)0));
3985 if (error != NULL((void *)0))
3986 goto done;
3987
3988 error = apply_unveil(got_repo_get_path(repo), 1,
3989 worktree ? got_worktree_get_root_path(worktree) : NULL((void *)0));
3990 if (error)
3991 goto done;
3992
3993 error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, NULL((void *)0));
3994 if (error)
3995 goto done;
3996
3997 error = got_reflist_object_id_map_create(&refs_idmap, &refs, repo);
3998 if (error)
3999 goto done;
4000
4001 if (start_commit == NULL((void *)0)) {
4002 struct got_reference *head_ref;
4003 struct got_commit_object *commit = NULL((void *)0);
4004 error = got_ref_open(&head_ref, repo,
4005 worktree ? got_worktree_get_head_ref_name(worktree)
4006 : GOT_REF_HEAD"HEAD", 0);
4007 if (error != NULL((void *)0))
4008 goto done;
4009 error = got_ref_resolve(&start_id, repo, head_ref);
4010 got_ref_close(head_ref);
4011 if (error != NULL((void *)0))
4012 goto done;
4013 error = got_object_open_as_commit(&commit, repo,
4014 start_id);
4015 if (error != NULL((void *)0))
4016 goto done;
4017 got_object_commit_close(commit);
4018 } else {
4019 error = got_repo_match_object_id(&start_id, NULL((void *)0),
4020 start_commit, GOT_OBJ_TYPE_COMMIT1, &refs, repo);
4021 if (error != NULL((void *)0))
4022 goto done;
4023 }
4024 if (end_commit != NULL((void *)0)) {
4025 error = got_repo_match_object_id(&end_id, NULL((void *)0),
4026 end_commit, GOT_OBJ_TYPE_COMMIT1, &refs, repo);
4027 if (error != NULL((void *)0))
4028 goto done;
4029 }
4030
4031 if (worktree) {
4032 /*
4033 * If a path was specified on the command line it was resolved
4034 * to a path in the work tree above. Prepend the work tree's
4035 * path prefix to obtain the corresponding in-repository path.
4036 */
4037 if (path) {
4038 const char *prefix;
4039 prefix = got_worktree_get_path_prefix(worktree);
4040 if (asprintf(&in_repo_path, "%s%s%s", prefix,
4041 (path[0] != '\0') ? "/" : "", path) == -1) {
4042 error = got_error_from_errno("asprintf");
4043 goto done;
4044 }
4045 }
4046 } else
4047 error = got_repo_map_path(&in_repo_path, repo,
4048 path ? path : "");
4049 if (error != NULL((void *)0))
4050 goto done;
4051 if (in_repo_path) {
4052 free(path);
4053 path = in_repo_path;
4054 }
4055
4056 error = print_commits(start_id, end_id, repo, path ? path : "",
4057 show_changed_paths, show_patch, search_pattern, diff_context,
4058 limit, log_branches, reverse_display_order, refs_idmap);
4059done:
4060 free(path);
4061 free(repo_path);
4062 free(cwd);
4063 if (worktree)
4064 got_worktree_close(worktree);
4065 if (repo) {
4066 const struct got_error *repo_error;
4067 repo_error = got_repo_close(repo);
4068 if (error == NULL((void *)0))
4069 error = repo_error;
4070 }
4071 if (refs_idmap)
4072 got_reflist_object_id_map_free(refs_idmap);
4073 got_ref_list_free(&refs);
4074 return error;
4075}
4076
4077__dead__attribute__((__noreturn__)) static void
4078usage_diff(void)
4079{
4080 fprintf(stderr(&__sF[2]), "usage: %s diff [-a] [-C number] [-r repository-path] "
4081 "[-s] [-w] [object1 object2 | path]\n", getprogname());
4082 exit(1);
4083}
4084
4085struct print_diff_arg {
4086 struct got_repository *repo;
4087 struct got_worktree *worktree;
4088 int diff_context;
4089 const char *id_str;
4090 int header_shown;
4091 int diff_staged;
4092 int ignore_whitespace;
4093 int force_text_diff;
4094};
4095
4096/*
4097 * Create a file which contains the target path of a symlink so we can feed
4098 * it as content to the diff engine.
4099 */
4100static const struct got_error *
4101get_symlink_target_file(int *fd, int dirfd, const char *de_name,
4102 const char *abspath)
4103{
4104 const struct got_error *err = NULL((void *)0);
4105 char target_path[PATH_MAX1024];
4106 ssize_t target_len, outlen;
4107
4108 *fd = -1;
4109
4110 if (dirfd != -1) {
4111 target_len = readlinkat(dirfd, de_name, target_path, PATH_MAX1024);
4112 if (target_len == -1)
4113 return got_error_from_errno2("readlinkat", abspath);
4114 } else {
4115 target_len = readlink(abspath, target_path, PATH_MAX1024);
4116 if (target_len == -1)
4117 return got_error_from_errno2("readlink", abspath);
4118 }
4119
4120 *fd = got_opentempfd();
4121 if (*fd == -1)
4122 return got_error_from_errno("got_opentempfd");
4123
4124 outlen = write(*fd, target_path, target_len);
4125 if (outlen == -1) {
4126 err = got_error_from_errno("got_opentempfd");
4127 goto done;
4128 }
4129
4130 if (lseek(*fd, 0, SEEK_SET0) == -1) {
4131 err = got_error_from_errno2("lseek", abspath);
4132 goto done;
4133 }
4134done:
4135 if (err) {
4136 close(*fd);
4137 *fd = -1;
4138 }
4139 return err;
4140}
4141
4142static const struct got_error *
4143print_diff(void *arg, unsigned char status, unsigned char staged_status,
4144 const char *path, struct got_object_id *blob_id,
4145 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
4146 int dirfd, const char *de_name)
4147{
4148 struct print_diff_arg *a = arg;
4149 const struct got_error *err = NULL((void *)0);
4150 struct got_blob_object *blob1 = NULL((void *)0);
4151 int fd = -1;
4152 FILE *f2 = NULL((void *)0);
4153 char *abspath = NULL((void *)0), *label1 = NULL((void *)0);
4154 struct stat sb;
4155
4156 if (a->diff_staged) {
4157 if (staged_status != GOT_STATUS_MODIFY'M' &&
4158 staged_status != GOT_STATUS_ADD'A' &&
4159 staged_status != GOT_STATUS_DELETE'D')
4160 return NULL((void *)0);
4161 } else {
4162 if (staged_status == GOT_STATUS_DELETE'D')
4163 return NULL((void *)0);
4164 if (status == GOT_STATUS_NONEXISTENT'N')
4165 return got_error_set_errno(ENOENT2, path);
4166 if (status != GOT_STATUS_MODIFY'M' &&
4167 status != GOT_STATUS_ADD'A' &&
4168 status != GOT_STATUS_DELETE'D' &&
4169 status != GOT_STATUS_CONFLICT'C')
4170 return NULL((void *)0);
4171 }
4172
4173 if (!a->header_shown) {
4174 printf("diff %s %s%s\n", a->id_str,
4175 got_worktree_get_root_path(a->worktree),
4176 a->diff_staged ? " (staged changes)" : "");
4177 a->header_shown = 1;
4178 }
4179
4180 if (a->diff_staged) {
4181 const char *label1 = NULL((void *)0), *label2 = NULL((void *)0);
4182 switch (staged_status) {
4183 case GOT_STATUS_MODIFY'M':
4184 label1 = path;
4185 label2 = path;
4186 break;
4187 case GOT_STATUS_ADD'A':
4188 label2 = path;
4189 break;
4190 case GOT_STATUS_DELETE'D':
4191 label1 = path;
4192 break;
4193 default:
4194 return got_error(GOT_ERR_FILE_STATUS68);
4195 }
4196 return got_diff_objects_as_blobs(NULL((void *)0), NULL((void *)0), blob_id,
4197 staged_blob_id, label1, label2, a->diff_context,
4198 a->ignore_whitespace, a->force_text_diff, a->repo, stdout(&__sF[1]));
4199 }
4200
4201 if (staged_status == GOT_STATUS_ADD'A' ||
4202 staged_status == GOT_STATUS_MODIFY'M') {
4203 char *id_str;
4204 err = got_object_open_as_blob(&blob1, a->repo, staged_blob_id,
4205 8192);
4206 if (err)
4207 goto done;
4208 err = got_object_id_str(&id_str, staged_blob_id);
4209 if (err)
4210 goto done;
4211 if (asprintf(&label1, "%s (staged)", id_str) == -1) {
4212 err = got_error_from_errno("asprintf");
4213 free(id_str);
4214 goto done;
4215 }
4216 free(id_str);
4217 } else if (status != GOT_STATUS_ADD'A') {
4218 err = got_object_open_as_blob(&blob1, a->repo, blob_id, 8192);
4219 if (err)
4220 goto done;
4221 }
4222
4223 if (status != GOT_STATUS_DELETE'D') {
4224 if (asprintf(&abspath, "%s/%s",
4225 got_worktree_get_root_path(a->worktree), path) == -1) {
4226 err = got_error_from_errno("asprintf");
4227 goto done;
4228 }
4229
4230 if (dirfd != -1) {
4231 fd = openat(dirfd, de_name, O_RDONLY0x0000 | O_NOFOLLOW0x0100);
4232 if (fd == -1) {
4233 if (errno(*__errno()) != ELOOP62) {
4234 err = got_error_from_errno2("openat",
4235 abspath);
4236 goto done;
4237 }
4238 err = get_symlink_target_file(&fd, dirfd,
4239 de_name, abspath);
4240 if (err)
4241 goto done;
4242 }
4243 } else {
4244 fd = open(abspath, O_RDONLY0x0000 | O_NOFOLLOW0x0100);
4245 if (fd == -1) {
4246 if (errno(*__errno()) != ELOOP62) {
4247 err = got_error_from_errno2("open",
4248 abspath);
4249 goto done;
4250 }
4251 err = get_symlink_target_file(&fd, dirfd,
4252 de_name, abspath);
4253 if (err)
4254 goto done;
4255 }
4256 }
4257 if (fstat(fd, &sb) == -1) {
4258 err = got_error_from_errno2("fstat", abspath);
4259 goto done;
4260 }
4261 f2 = fdopen(fd, "r");
4262 if (f2 == NULL((void *)0)) {
4263 err = got_error_from_errno2("fdopen", abspath);
4264 goto done;
4265 }
4266 fd = -1;
4267 } else
4268 sb.st_size = 0;
4269
4270 err = got_diff_blob_file(blob1, label1, f2, sb.st_size, path,
4271 a->diff_context, a->ignore_whitespace, a->force_text_diff, stdout(&__sF[1]));
4272done:
4273 if (blob1)
4274 got_object_blob_close(blob1);
4275 if (f2 && fclose(f2) == EOF(-1) && err == NULL((void *)0))
4276 err = got_error_from_errno("fclose");
4277 if (fd != -1 && close(fd) == -1 && err == NULL((void *)0))
4278 err = got_error_from_errno("close");
4279 free(abspath);
4280 return err;
4281}
4282
4283static const struct got_error *
4284cmd_diff(int argc, char *argv[])
4285{
4286 const struct got_error *error;
4287 struct got_repository *repo = NULL((void *)0);
4288 struct got_worktree *worktree = NULL((void *)0);
4289 char *cwd = NULL((void *)0), *repo_path = NULL((void *)0);
4290 struct got_object_id *id1 = NULL((void *)0), *id2 = NULL((void *)0);
4291 const char *id_str1 = NULL((void *)0), *id_str2 = NULL((void *)0);
4292 char *label1 = NULL((void *)0), *label2 = NULL((void *)0);
4293 int type1, type2;
4294 int diff_context = 3, diff_staged = 0, ignore_whitespace = 0, ch;
4295 int force_text_diff = 0;
4296 const char *errstr;
4297 char *path = NULL((void *)0);
4298 struct got_reflist_head refs;
4299
4300 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
4301
4302#ifndef PROFILE
4303 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4304 NULL((void *)0)) == -1)
4305 err(1, "pledge");
4306#endif
4307
4308 while ((ch = getopt(argc, argv, "aC:r:sw")) != -1) {
4309 switch (ch) {
4310 case 'a':
4311 force_text_diff = 1;
4312 break;
4313 case 'C':
4314 diff_context = strtonum(optarg, 0, GOT_DIFF_MAX_CONTEXT64,
4315 &errstr);
4316 if (errstr != NULL((void *)0))
4317 err(1, "-C option %s", errstr);
4318 break;
4319 case 'r':
4320 repo_path = realpath(optarg, NULL((void *)0));
4321 if (repo_path == NULL((void *)0))
4322 return got_error_from_errno2("realpath",
4323 optarg);
4324 got_path_strip_trailing_slashes(repo_path);
4325 break;
4326 case 's':
4327 diff_staged = 1;
4328 break;
4329 case 'w':
4330 ignore_whitespace = 1;
4331 break;
4332 default:
4333 usage_diff();
4334 /* NOTREACHED */
4335 }
4336 }
4337
4338 argc -= optind;
4339 argv += optind;
4340
4341 cwd = getcwd(NULL((void *)0), 0);
4342 if (cwd == NULL((void *)0)) {
4343 error = got_error_from_errno("getcwd");
4344 goto done;
4345 }
4346 if (argc <= 1) {
4347 if (repo_path)
4348 errx(1,
4349 "-r option can't be used when diffing a work tree");
4350 error = got_worktree_open(&worktree, cwd);
4351 if (error) {
4352 if (error->code == GOT_ERR_NOT_WORKTREE60)
4353 error = wrap_not_worktree_error(error, "diff",
4354 cwd);
4355 goto done;
4356 }
4357 repo_path = strdup(got_worktree_get_repo_path(worktree));
4358 if (repo_path == NULL((void *)0)) {
4359 error = got_error_from_errno("strdup");
4360 goto done;
4361 }
4362 if (argc == 1) {
4363 error = got_worktree_resolve_path(&path, worktree,
4364 argv[0]);
4365 if (error)
4366 goto done;
4367 } else {
4368 path = strdup("");
4369 if (path == NULL((void *)0)) {
4370 error = got_error_from_errno("strdup");
4371 goto done;
4372 }
4373 }
4374 } else if (argc == 2) {
4375 if (diff_staged)
4376 errx(1, "-s option can't be used when diffing "
4377 "objects in repository");
4378 id_str1 = argv[0];
4379 id_str2 = argv[1];
4380 if (repo_path == NULL((void *)0)) {
4381 error = got_worktree_open(&worktree, cwd);
4382 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
4383 goto done;
4384 repo_path = strdup(worktree ?
4385 got_worktree_get_repo_path(worktree) : cwd);
4386 if (repo_path == NULL((void *)0)) {
4387 error = got_error_from_errno("strdup");
4388 goto done;
4389 }
4390 }
4391 } else
4392 usage_diff();
4393
4394 error = got_repo_open(&repo, repo_path, NULL((void *)0));
4395 free(repo_path);
4396 if (error != NULL((void *)0))
4397 goto done;
4398
4399 error = apply_unveil(got_repo_get_path(repo), 1,
4400 worktree ? got_worktree_get_root_path(worktree) : NULL((void *)0));
4401 if (error)
4402 goto done;
4403
4404 if (argc <= 1) {
4405 struct print_diff_arg arg;
4406 struct got_pathlist_head paths;
4407 char *id_str;
4408
4409 TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)->
tqh_last = &(&paths)->tqh_first; } while (0)
;
4410
4411 error = got_object_id_str(&id_str,
4412 got_worktree_get_base_commit_id(worktree));
4413 if (error)
4414 goto done;
4415 arg.repo = repo;
4416 arg.worktree = worktree;
4417 arg.diff_context = diff_context;
4418 arg.id_str = id_str;
4419 arg.header_shown = 0;
4420 arg.diff_staged = diff_staged;
4421 arg.ignore_whitespace = ignore_whitespace;
4422 arg.force_text_diff = force_text_diff;
4423
4424 error = got_pathlist_append(&paths, path, NULL((void *)0));
4425 if (error)
4426 goto done;
4427
4428 error = got_worktree_status(worktree, &paths, repo, print_diff,
4429 &arg, check_cancelled, NULL((void *)0));
4430 free(id_str);
4431 got_pathlist_free(&paths);
4432 goto done;
4433 }
4434
4435 error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, NULL((void *)0));
4436 if (error)
4437 return error;
4438
4439 error = got_repo_match_object_id(&id1, &label1, id_str1,
4440 GOT_OBJ_TYPE_ANY0, &refs, repo);
4441 if (error)
4442 goto done;
4443
4444 error = got_repo_match_object_id(&id2, &label2, id_str2,
4445 GOT_OBJ_TYPE_ANY0, &refs, repo);
4446 if (error)
4447 goto done;
4448
4449 error = got_object_get_type(&type1, repo, id1);
4450 if (error)
4451 goto done;
4452
4453 error = got_object_get_type(&type2, repo, id2);
4454 if (error)
4455 goto done;
4456
4457 if (type1 != type2) {
4458 error = got_error(GOT_ERR_OBJ_TYPE11);
4459 goto done;
4460 }
4461
4462 switch (type1) {
4463 case GOT_OBJ_TYPE_BLOB3:
4464 error = got_diff_objects_as_blobs(NULL((void *)0), NULL((void *)0), id1, id2,
4465 NULL((void *)0), NULL((void *)0), diff_context, ignore_whitespace,
4466 force_text_diff, repo, stdout(&__sF[1]));
4467 break;
4468 case GOT_OBJ_TYPE_TREE2:
4469 error = got_diff_objects_as_trees(NULL((void *)0), NULL((void *)0), id1, id2,
4470 "", "", diff_context, ignore_whitespace, force_text_diff,
4471 repo, stdout(&__sF[1]));
4472 break;
4473 case GOT_OBJ_TYPE_COMMIT1:
4474 printf("diff %s %s\n", label1, label2);
4475 error = got_diff_objects_as_commits(NULL((void *)0), NULL((void *)0), id1, id2,
4476 diff_context, ignore_whitespace, force_text_diff, repo,
4477 stdout(&__sF[1]));
4478 break;
4479 default:
4480 error = got_error(GOT_ERR_OBJ_TYPE11);
4481 }
4482done:
4483 free(label1);
4484 free(label2);
4485 free(id1);
4486 free(id2);
4487 free(path);
4488 if (worktree)
4489 got_worktree_close(worktree);
4490 if (repo) {
4491 const struct got_error *repo_error;
4492 repo_error = got_repo_close(repo);
4493 if (error == NULL((void *)0))
4494 error = repo_error;
4495 }
4496 got_ref_list_free(&refs);
4497 return error;
4498}
4499
4500__dead__attribute__((__noreturn__)) static void
4501usage_blame(void)
4502{
4503 fprintf(stderr(&__sF[2]),
4504 "usage: %s blame [-c commit] [-r repository-path] path\n",
4505 getprogname());
4506 exit(1);
4507}
4508
4509struct blame_line {
4510 int annotated;
4511 char *id_str;
4512 char *committer;
4513 char datebuf[11]; /* YYYY-MM-DD + NUL */
4514};
4515
4516struct blame_cb_args {
4517 struct blame_line *lines;
4518 int nlines;
4519 int nlines_prec;
4520 int lineno_cur;
4521 off_t *line_offsets;
4522 FILE *f;
4523 struct got_repository *repo;
4524};
4525
4526static const struct got_error *
4527blame_cb(void *arg, int nlines, int lineno, struct got_object_id *id)
4528{
4529 const struct got_error *err = NULL((void *)0);
4530 struct blame_cb_args *a = arg;
4531 struct blame_line *bline;
4532 char *line = NULL((void *)0);
4533 size_t linesize = 0;
4534 struct got_commit_object *commit = NULL((void *)0);
4535 off_t offset;
4536 struct tm tm;
4537 time_t committer_time;
4538
4539 if (nlines != a->nlines ||
4540 (lineno != -1 && lineno < 1) || lineno > a->nlines)
4541 return got_error(GOT_ERR_RANGE47);
4542
4543 if (sigint_received)
4544 return got_error(GOT_ERR_ITER_COMPLETED46);
4545
4546 if (lineno == -1)
4547 return NULL((void *)0); /* no change in this commit */
4548
4549 /* Annotate this line. */
4550 bline = &a->lines[lineno - 1];
4551 if (bline->annotated)
4552 return NULL((void *)0);
4553 err = got_object_id_str(&bline->id_str, id);
4554 if (err)
4555 return err;
4556
4557 err = got_object_open_as_commit(&commit, a->repo, id);
4558 if (err)
4559 goto done;
4560
4561 bline->committer = strdup(got_object_commit_get_committer(commit));
4562 if (bline->committer == NULL((void *)0)) {
4563 err = got_error_from_errno("strdup");
4564 goto done;
4565 }
4566
4567 committer_time = got_object_commit_get_committer_time(commit);
4568 if (localtime_r(&committer_time, &tm) == NULL((void *)0))
4569 return got_error_from_errno("localtime_r");
4570 if (strftime(bline->datebuf, sizeof(bline->datebuf), "%G-%m-%d",
4571 &tm) == 0) {
4572 err = got_error(GOT_ERR_NO_SPACE9);
4573 goto done;
4574 }
4575 bline->annotated = 1;
4576
4577 /* Print lines annotated so far. */
4578 bline = &a->lines[a->lineno_cur - 1];
4579 if (!bline->annotated)
4580 goto done;
4581
4582 offset = a->line_offsets[a->lineno_cur - 1];
4583 if (fseeko(a->f, offset, SEEK_SET0) == -1) {
4584 err = got_error_from_errno("fseeko");
4585 goto done;
4586 }
4587
4588 while (bline->annotated) {
4589 char *smallerthan, *at, *nl, *committer;
4590 size_t len;
4591
4592 if (getline(&line, &linesize, a->f) == -1) {
4593 if (ferror(a->f)(!__isthreaded ? (((a->f)->_flags & 0x0040) != 0) :
(ferror)(a->f))
)
4594 err = got_error_from_errno("getline");
4595 break;
4596 }
4597
4598 committer = bline->committer;
4599 smallerthan = strchr(committer, '<');
4600 if (smallerthan && smallerthan[1] != '\0')
4601 committer = smallerthan + 1;
4602 at = strchr(committer, '@');
4603 if (at)
4604 *at = '\0';
4605 len = strlen(committer);
4606 if (len >= 9)
4607 committer[8] = '\0';
4608
4609 nl = strchr(line, '\n');
4610 if (nl)
4611 *nl = '\0';
4612 printf("%.*d) %.8s %s %-8s %s\n", a->nlines_prec, a->lineno_cur,
4613 bline->id_str, bline->datebuf, committer, line);
4614
4615 a->lineno_cur++;
4616 bline = &a->lines[a->lineno_cur - 1];
4617 }
4618done:
4619 if (commit)
4620 got_object_commit_close(commit);
4621 free(line);
4622 return err;
4623}
4624
4625static const struct got_error *
4626cmd_blame(int argc, char *argv[])
4627{
4628 const struct got_error *error;
4629 struct got_repository *repo = NULL((void *)0);
4630 struct got_worktree *worktree = NULL((void *)0);
4631 char *path, *cwd = NULL((void *)0), *repo_path = NULL((void *)0), *in_repo_path = NULL((void *)0);
4632 char *link_target = NULL((void *)0);
4633 struct got_object_id *obj_id = NULL((void *)0);
4634 struct got_object_id *commit_id = NULL((void *)0);
4635 struct got_blob_object *blob = NULL((void *)0);
4636 char *commit_id_str = NULL((void *)0);
4637 struct blame_cb_args bca;
4638 int ch, obj_type, i;
4639 off_t filesize;
4640
4641 memset(&bca, 0, sizeof(bca));
4642
4643#ifndef PROFILE
4644 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4645 NULL((void *)0)) == -1)
4646 err(1, "pledge");
4647#endif
4648
4649 while ((ch = getopt(argc, argv, "c:r:")) != -1) {
4650 switch (ch) {
4651 case 'c':
4652 commit_id_str = optarg;
4653 break;
4654 case 'r':
4655 repo_path = realpath(optarg, NULL((void *)0));
4656 if (repo_path == NULL((void *)0))
4657 return got_error_from_errno2("realpath",
4658 optarg);
4659 got_path_strip_trailing_slashes(repo_path);
4660 break;
4661 default:
4662 usage_blame();
4663 /* NOTREACHED */
4664 }
4665 }
4666
4667 argc -= optind;
4668 argv += optind;
4669
4670 if (argc == 1)
4671 path = argv[0];
4672 else
4673 usage_blame();
4674
4675 cwd = getcwd(NULL((void *)0), 0);
4676 if (cwd == NULL((void *)0)) {
4677 error = got_error_from_errno("getcwd");
4678 goto done;
4679 }
4680 if (repo_path == NULL((void *)0)) {
4681 error = got_worktree_open(&worktree, cwd);
4682 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
4683 goto done;
4684 else
4685 error = NULL((void *)0);
4686 if (worktree) {
4687 repo_path =
4688 strdup(got_worktree_get_repo_path(worktree));
4689 if (repo_path == NULL((void *)0)) {
4690 error = got_error_from_errno("strdup");
4691 if (error)
4692 goto done;
4693 }
4694 } else {
4695 repo_path = strdup(cwd);
4696 if (repo_path == NULL((void *)0)) {
4697 error = got_error_from_errno("strdup");
4698 goto done;
4699 }
4700 }
4701 }
4702
4703 error = got_repo_open(&repo, repo_path, NULL((void *)0));
4704 if (error != NULL((void *)0))
4705 goto done;
4706
4707 if (worktree) {
4708 const char *prefix = got_worktree_get_path_prefix(worktree);
4709 char *p;
4710
4711 error = got_worktree_resolve_path(&p, worktree, path);
4712 if (error)
4713 goto done;
4714 if (asprintf(&in_repo_path, "%s%s%s", prefix,
4715 (p[0] != '\0' && !got_path_is_root_dir(prefix)) ? "/" : "",
4716 p) == -1) {
4717 error = got_error_from_errno("asprintf");
4718 free(p);
4719 goto done;
4720 }
4721 free(p);
4722 error = apply_unveil(got_repo_get_path(repo), 1, NULL((void *)0));
4723 } else {
4724 error = apply_unveil(got_repo_get_path(repo), 1, NULL((void *)0));
4725 if (error)
4726 goto done;
4727 error = got_repo_map_path(&in_repo_path, repo, path);
4728 }
4729 if (error)
4730 goto done;
4731
4732 if (commit_id_str == NULL((void *)0)) {
4733 struct got_reference *head_ref;
4734 error = got_ref_open(&head_ref, repo, worktree ?
4735 got_worktree_get_head_ref_name(worktree) : GOT_REF_HEAD"HEAD", 0);
4736 if (error != NULL((void *)0))
4737 goto done;
4738 error = got_ref_resolve(&commit_id, repo, head_ref);
4739 got_ref_close(head_ref);
4740 if (error != NULL((void *)0))
4741 goto done;
4742 } else {
4743 struct got_reflist_head refs;
4744 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
4745 error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name,
4746 NULL((void *)0));
4747 if (error)
4748 goto done;
4749 error = got_repo_match_object_id(&commit_id, NULL((void *)0),
4750 commit_id_str, GOT_OBJ_TYPE_COMMIT1, &refs, repo);
4751 got_ref_list_free(&refs);
4752 if (error)
4753 goto done;
4754 }
4755
4756 error = got_object_resolve_symlinks(&link_target, in_repo_path,
4757 commit_id, repo);
4758 if (error)
4759 goto done;
4760
4761 error = got_object_id_by_path(&obj_id, repo, commit_id,
4762 link_target ? link_target : in_repo_path);
4763 if (error)
4764 goto done;
4765
4766 error = got_object_get_type(&obj_type, repo, obj_id);
4767 if (error)
4768 goto done;
4769
4770 if (obj_type != GOT_OBJ_TYPE_BLOB3) {
4771 error = got_error_path(link_target ? link_target : in_repo_path,
4772 GOT_ERR_OBJ_TYPE11);
4773 goto done;
4774 }
4775
4776 error = got_object_open_as_blob(&blob, repo, obj_id, 8192);
4777 if (error)
4778 goto done;
4779 bca.f = got_opentemp();
4780 if (bca.f == NULL((void *)0)) {
4781 error = got_error_from_errno("got_opentemp");
4782 goto done;
4783 }
4784 error = got_object_blob_dump_to_file(&filesize, &bca.nlines,
4785 &bca.line_offsets, bca.f, blob);
4786 if (error || bca.nlines == 0)
4787 goto done;
4788
4789 /* Don't include \n at EOF in the blame line count. */
4790 if (bca.line_offsets[bca.nlines - 1] == filesize)
4791 bca.nlines--;
4792
4793 bca.lines = calloc(bca.nlines, sizeof(*bca.lines));
4794 if (bca.lines == NULL((void *)0)) {
4795 error = got_error_from_errno("calloc");
4796 goto done;
4797 }
4798 bca.lineno_cur = 1;
4799 bca.nlines_prec = 0;
4800 i = bca.nlines;
4801 while (i > 0) {
4802 i /= 10;
4803 bca.nlines_prec++;
4804 }
4805 bca.repo = repo;
4806
4807 error = got_blame(link_target ? link_target : in_repo_path, commit_id,
4808 repo, blame_cb, &bca, check_cancelled, NULL((void *)0));
4809done:
4810 free(in_repo_path);
4811 free(link_target);
4812 free(repo_path);
4813 free(cwd);
4814 free(commit_id);
4815 free(obj_id);
4816 if (blob)
4817 got_object_blob_close(blob);
4818 if (worktree)
4819 got_worktree_close(worktree);
4820 if (repo) {
4821 const struct got_error *repo_error;
4822 repo_error = got_repo_close(repo);
4823 if (error == NULL((void *)0))
4824 error = repo_error;
4825 }
4826 if (bca.lines) {
4827 for (i = 0; i < bca.nlines; i++) {
4828 struct blame_line *bline = &bca.lines[i];
4829 free(bline->id_str);
4830 free(bline->committer);
4831 }
4832 free(bca.lines);
4833 }
4834 free(bca.line_offsets);
4835 if (bca.f && fclose(bca.f) == EOF(-1) && error == NULL((void *)0))
4836 error = got_error_from_errno("fclose");
4837 return error;
4838}
4839
4840__dead__attribute__((__noreturn__)) static void
4841usage_tree(void)
4842{
4843 fprintf(stderr(&__sF[2]),
4844 "usage: %s tree [-c commit] [-r repository-path] [-iR] [path]\n",
4845 getprogname());
4846 exit(1);
4847}
4848
4849static const struct got_error *
4850print_entry(struct got_tree_entry *te, const char *id, const char *path,
4851 const char *root_path, struct got_repository *repo)
4852{
4853 const struct got_error *err = NULL((void *)0);
4854 int is_root_path = (strcmp(path, root_path) == 0);
4855 const char *modestr = "";
4856 mode_t mode = got_tree_entry_get_mode(te);
4857 char *link_target = NULL((void *)0);
4858
4859 path += strlen(root_path);
4860 while (path[0] == '/')
4861 path++;
4862
4863 if (got_object_tree_entry_is_submodule(te))
4864 modestr = "$";
4865 else if (S_ISLNK(mode)((mode & 0170000) == 0120000)) {
4866 int i;
4867
4868 err = got_tree_entry_get_symlink_target(&link_target, te, repo);
4869 if (err)
4870 return err;
4871 for (i = 0; i < strlen(link_target); i++) {
4872 if (!isprint((unsigned char)link_target[i]))
4873 link_target[i] = '?';
4874 }
4875
4876 modestr = "@";
4877 }
4878 else if (S_ISDIR(mode)((mode & 0170000) == 0040000))
4879 modestr = "/";
4880 else if (mode & S_IXUSR0000100)
4881 modestr = "*";
4882
4883 printf("%s%s%s%s%s%s%s\n", id ? id : "", path,
4884 is_root_path ? "" : "/", got_tree_entry_get_name(te), modestr,
4885 link_target ? " -> ": "", link_target ? link_target : "");
4886
4887 free(link_target);
4888 return NULL((void *)0);
4889}
4890
4891static const struct got_error *
4892print_tree(const char *path, struct got_object_id *commit_id,
4893 int show_ids, int recurse, const char *root_path,
4894 struct got_repository *repo)
4895{
4896 const struct got_error *err = NULL((void *)0);
4897 struct got_object_id *tree_id = NULL((void *)0);
4898 struct got_tree_object *tree = NULL((void *)0);
4899 int nentries, i;
4900
4901 err = got_object_id_by_path(&tree_id, repo, commit_id, path);
4902 if (err)
4903 goto done;
4904
4905 err = got_object_open_as_tree(&tree, repo, tree_id);
4906 if (err)
4907 goto done;
4908 nentries = got_object_tree_get_nentries(tree);
4909 for (i = 0; i < nentries; i++) {
4910 struct got_tree_entry *te;
4911 char *id = NULL((void *)0);
4912
4913 if (sigint_received || sigpipe_received)
4914 break;
4915
4916 te = got_object_tree_get_entry(tree, i);
4917 if (show_ids) {
4918 char *id_str;
4919 err = got_object_id_str(&id_str,
4920 got_tree_entry_get_id(te));
4921 if (err)
4922 goto done;
4923 if (asprintf(&id, "%s ", id_str) == -1) {
4924 err = got_error_from_errno("asprintf");
4925 free(id_str);
4926 goto done;
4927 }
4928 free(id_str);
4929 }
4930 err = print_entry(te, id, path, root_path, repo);
4931 free(id);
4932 if (err)
4933 goto done;
4934
4935 if (recurse && S_ISDIR(got_tree_entry_get_mode(te))((got_tree_entry_get_mode(te) & 0170000) == 0040000)) {
4936 char *child_path;
4937 if (asprintf(&child_path, "%s%s%s", path,
4938 path[0] == '/' && path[1] == '\0' ? "" : "/",
4939 got_tree_entry_get_name(te)) == -1) {
4940 err = got_error_from_errno("asprintf");
4941 goto done;
4942 }
4943 err = print_tree(child_path, commit_id, show_ids, 1,
4944 root_path, repo);
4945 free(child_path);
4946 if (err)
4947 goto done;
4948 }
4949 }
4950done:
4951 if (tree)
4952 got_object_tree_close(tree);
4953 free(tree_id);
4954 return err;
4955}
4956
4957static const struct got_error *
4958cmd_tree(int argc, char *argv[])
4959{
4960 const struct got_error *error;
4961 struct got_repository *repo = NULL((void *)0);
4962 struct got_worktree *worktree = NULL((void *)0);
4963 const char *path, *refname = NULL((void *)0);
4964 char *cwd = NULL((void *)0), *repo_path = NULL((void *)0), *in_repo_path = NULL((void *)0);
4965 struct got_object_id *commit_id = NULL((void *)0);
4966 char *commit_id_str = NULL((void *)0);
4967 int show_ids = 0, recurse = 0;
4968 int ch;
4969
4970#ifndef PROFILE
4971 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
4972 NULL((void *)0)) == -1)
4973 err(1, "pledge");
4974#endif
4975
4976 while ((ch = getopt(argc, argv, "c:r:iR")) != -1) {
4977 switch (ch) {
4978 case 'c':
4979 commit_id_str = optarg;
4980 break;
4981 case 'r':
4982 repo_path = realpath(optarg, NULL((void *)0));
4983 if (repo_path == NULL((void *)0))
4984 return got_error_from_errno2("realpath",
4985 optarg);
4986 got_path_strip_trailing_slashes(repo_path);
4987 break;
4988 case 'i':
4989 show_ids = 1;
4990 break;
4991 case 'R':
4992 recurse = 1;
4993 break;
4994 default:
4995 usage_tree();
4996 /* NOTREACHED */
4997 }
4998 }
4999
5000 argc -= optind;
5001 argv += optind;
5002
5003 if (argc == 1)
5004 path = argv[0];
5005 else if (argc > 1)
5006 usage_tree();
5007 else
5008 path = NULL((void *)0);
5009
5010 cwd = getcwd(NULL((void *)0), 0);
5011 if (cwd == NULL((void *)0)) {
5012 error = got_error_from_errno("getcwd");
5013 goto done;
5014 }
5015 if (repo_path == NULL((void *)0)) {
5016 error = got_worktree_open(&worktree, cwd);
5017 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
5018 goto done;
5019 else
5020 error = NULL((void *)0);
5021 if (worktree) {
5022 repo_path =
5023 strdup(got_worktree_get_repo_path(worktree));
5024 if (repo_path == NULL((void *)0))
5025 error = got_error_from_errno("strdup");
5026 if (error)
5027 goto done;
5028 } else {
5029 repo_path = strdup(cwd);
5030 if (repo_path == NULL((void *)0)) {
5031 error = got_error_from_errno("strdup");
5032 goto done;
5033 }
5034 }
5035 }
5036
5037 error = got_repo_open(&repo, repo_path, NULL((void *)0));
5038 if (error != NULL((void *)0))
5039 goto done;
5040
5041 if (worktree) {
5042 const char *prefix = got_worktree_get_path_prefix(worktree);
5043 char *p;
5044
5045 if (path == NULL((void *)0))
5046 path = "";
5047 error = got_worktree_resolve_path(&p, worktree, path);
5048 if (error)
5049 goto done;
5050 if (asprintf(&in_repo_path, "%s%s%s", prefix,
5051 (p[0] != '\0' && !got_path_is_root_dir(prefix)) ? "/" : "",
5052 p) == -1) {
5053 error = got_error_from_errno("asprintf");
5054 free(p);
5055 goto done;
5056 }
5057 free(p);
5058 error = apply_unveil(got_repo_get_path(repo), 1, NULL((void *)0));
5059 if (error)
5060 goto done;
5061 } else {
5062 error = apply_unveil(got_repo_get_path(repo), 1, NULL((void *)0));
5063 if (error)
5064 goto done;
5065 if (path == NULL((void *)0))
5066 path = "/";
5067 error = got_repo_map_path(&in_repo_path, repo, path);
5068 if (error != NULL((void *)0))
5069 goto done;
5070 }
5071
5072 if (commit_id_str == NULL((void *)0)) {
5073 struct got_reference *head_ref;
5074 if (worktree)
5075 refname = got_worktree_get_head_ref_name(worktree);
5076 else
5077 refname = GOT_REF_HEAD"HEAD";
5078 error = got_ref_open(&head_ref, repo, refname, 0);
5079 if (error != NULL((void *)0))
5080 goto done;
5081 error = got_ref_resolve(&commit_id, repo, head_ref);
5082 got_ref_close(head_ref);
5083 if (error != NULL((void *)0))
5084 goto done;
5085 } else {
5086 struct got_reflist_head refs;
5087 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
5088 error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name,
5089 NULL((void *)0));
5090 if (error)
5091 goto done;
5092 error = got_repo_match_object_id(&commit_id, NULL((void *)0),
5093 commit_id_str, GOT_OBJ_TYPE_COMMIT1, &refs, repo);
5094 got_ref_list_free(&refs);
5095 if (error)
5096 goto done;
5097 }
5098
5099 error = print_tree(in_repo_path, commit_id, show_ids, recurse,
5100 in_repo_path, repo);
5101done:
5102 free(in_repo_path);
5103 free(repo_path);
5104 free(cwd);
5105 free(commit_id);
5106 if (worktree)
5107 got_worktree_close(worktree);
5108 if (repo) {
5109 const struct got_error *repo_error;
5110 repo_error = got_repo_close(repo);
5111 if (error == NULL((void *)0))
5112 error = repo_error;
5113 }
5114 return error;
5115}
5116
5117__dead__attribute__((__noreturn__)) static void
5118usage_status(void)
5119{
5120 fprintf(stderr(&__sF[2]), "usage: %s status [-s status-codes ] [path ...]\n",
5121 getprogname());
5122 exit(1);
5123}
5124
5125static const struct got_error *
5126print_status(void *arg, unsigned char status, unsigned char staged_status,
5127 const char *path, struct got_object_id *blob_id,
5128 struct got_object_id *staged_blob_id, struct got_object_id *commit_id,
5129 int dirfd, const char *de_name)
5130{
5131 if (status == staged_status && (status == GOT_STATUS_DELETE'D'))
5132 status = GOT_STATUS_NO_CHANGE' ';
5133 if (arg) {
5134 char *status_codes = arg;
5135 size_t ncodes = strlen(status_codes);
5136 int i;
5137 for (i = 0; i < ncodes ; i++) {
5138 if (status == status_codes[i] ||
5139 staged_status == status_codes[i])
5140 break;
5141 }
5142 if (i == ncodes)
5143 return NULL((void *)0);
5144 }
5145 printf("%c%c %s\n", status, staged_status, path);
5146 return NULL((void *)0);
5147}
5148
5149static const struct got_error *
5150cmd_status(int argc, char *argv[])
5151{
5152 const struct got_error *error = NULL((void *)0);
5153 struct got_repository *repo = NULL((void *)0);
5154 struct got_worktree *worktree = NULL((void *)0);
5155 char *cwd = NULL((void *)0), *status_codes = NULL((void *)0);;
5156 struct got_pathlist_head paths;
5157 struct got_pathlist_entry *pe;
5158 int ch, i;
5159
5160 TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)->
tqh_last = &(&paths)->tqh_first; } while (0)
;
5161
5162 while ((ch = getopt(argc, argv, "s:")) != -1) {
5163 switch (ch) {
5164 case 's':
5165 for (i = 0; i < strlen(optarg); i++) {
5166 switch (optarg[i]) {
5167 case GOT_STATUS_MODIFY'M':
5168 case GOT_STATUS_ADD'A':
5169 case GOT_STATUS_DELETE'D':
5170 case GOT_STATUS_CONFLICT'C':
5171 case GOT_STATUS_MISSING'!':
5172 case GOT_STATUS_OBSTRUCTED'~':
5173 case GOT_STATUS_UNVERSIONED'?':
5174 case GOT_STATUS_MODE_CHANGE'm':
5175 case GOT_STATUS_NONEXISTENT'N':
5176 break;
5177 default:
5178 errx(1, "invalid status code '%c'",
5179 optarg[i]);
5180 }
5181 }
5182 status_codes = optarg;
5183 break;
5184 default:
5185 usage_status();
5186 /* NOTREACHED */
5187 }
5188 }
5189
5190 argc -= optind;
5191 argv += optind;
5192
5193#ifndef PROFILE
5194 if (pledge("stdio rpath wpath cpath flock proc exec sendfd unveil",
5195 NULL((void *)0)) == -1)
5196 err(1, "pledge");
5197#endif
5198 cwd = getcwd(NULL((void *)0), 0);
5199 if (cwd == NULL((void *)0)) {
5200 error = got_error_from_errno("getcwd");
5201 goto done;
5202 }
5203
5204 error = got_worktree_open(&worktree, cwd);
5205 if (error) {
5206 if (error->code == GOT_ERR_NOT_WORKTREE60)
5207 error = wrap_not_worktree_error(error, "status", cwd);
5208 goto done;
5209 }
5210
5211 error = got_repo_open(&repo, got_worktree_get_repo_path(worktree),
5212 NULL((void *)0));
5213 if (error != NULL((void *)0))
5214 goto done;
5215
5216 error = apply_unveil(got_repo_get_path(repo), 1,
5217 got_worktree_get_root_path(worktree));
5218 if (error)
5219 goto done;
5220
5221 error = get_worktree_paths_from_argv(&paths, argc, argv, worktree);
5222 if (error)
5223 goto done;
5224
5225 error = got_worktree_status(worktree, &paths, repo, print_status,
5226 status_codes, check_cancelled, NULL((void *)0));
5227done:
5228 TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0);
(pe) = ((pe)->entry.tqe_next))
5229 free((char *)pe->path);
5230 got_pathlist_free(&paths);
5231 free(cwd);
5232 return error;
5233}
5234
5235__dead__attribute__((__noreturn__)) static void
5236usage_ref(void)
5237{
5238 fprintf(stderr(&__sF[2]),
5239 "usage: %s ref [-r repository] [-l] [-c object] [-s reference] "
5240 "[-d] [name]\n",
5241 getprogname());
5242 exit(1);
5243}
5244
5245static const struct got_error *
5246list_refs(struct got_repository *repo, const char *refname)
5247{
5248 static const struct got_error *err = NULL((void *)0);
5249 struct got_reflist_head refs;
5250 struct got_reflist_entry *re;
5251
5252 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
5253 err = got_ref_list(&refs, repo, refname, got_ref_cmp_by_name, NULL((void *)0));
5254 if (err)
5255 return err;
5256
5257 TAILQ_FOREACH(re, &refs, entry)for((re) = ((&refs)->tqh_first); (re) != ((void *)0); (
re) = ((re)->entry.tqe_next))
{
5258 char *refstr;
5259 refstr = got_ref_to_str(re->ref);
5260 if (refstr == NULL((void *)0))
5261 return got_error_from_errno("got_ref_to_str");
5262 printf("%s: %s\n", got_ref_get_name(re->ref), refstr);
5263 free(refstr);
5264 }
5265
5266 got_ref_list_free(&refs);
5267 return NULL((void *)0);
5268}
5269
5270static const struct got_error *
5271delete_ref(struct got_repository *repo, const char *refname)
5272{
5273 const struct got_error *err = NULL((void *)0);
5274 struct got_reference *ref;
5275
5276 err = got_ref_open(&ref, repo, refname, 0);
5277 if (err)
5278 return err;
5279
5280 err = got_ref_delete(ref, repo);
5281 got_ref_close(ref);
5282 return err;
5283}
5284
5285static const struct got_error *
5286add_ref(struct got_repository *repo, const char *refname, const char *target)
5287{
5288 const struct got_error *err = NULL((void *)0);
5289 struct got_object_id *id;
5290 struct got_reference *ref = NULL((void *)0);
5291
5292 /*
5293 * Don't let the user create a reference name with a leading '-'.
5294 * While technically a valid reference name, this case is usually
5295 * an unintended typo.
5296 */
5297 if (refname[0] == '-')
5298 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS113);
5299
5300 err = got_repo_match_object_id_prefix(&id, target, GOT_OBJ_TYPE_ANY0,
5301 repo);
5302 if (err) {
5303 struct got_reference *target_ref;
5304
5305 if (err->code != GOT_ERR_BAD_OBJ_ID_STR23)
5306 return err;
5307 err = got_ref_open(&target_ref, repo, target, 0);
5308 if (err)
5309 return err;
5310 err = got_ref_resolve(&id, repo, target_ref);
5311 got_ref_close(target_ref);
5312 if (err)
5313 return err;
5314 }
5315
5316 err = got_ref_alloc(&ref, refname, id);
5317 if (err)
5318 goto done;
5319
5320 err = got_ref_write(ref, repo);
5321done:
5322 if (ref)
5323 got_ref_close(ref);
5324 free(id);
5325 return err;
5326}
5327
5328static const struct got_error *
5329add_symref(struct got_repository *repo, const char *refname, const char *target)
5330{
5331 const struct got_error *err = NULL((void *)0);
5332 struct got_reference *ref = NULL((void *)0);
5333 struct got_reference *target_ref = NULL((void *)0);
5334
5335 /*
5336 * Don't let the user create a reference name with a leading '-'.
5337 * While technically a valid reference name, this case is usually
5338 * an unintended typo.
5339 */
5340 if (refname[0] == '-')
5341 return got_error_path(refname, GOT_ERR_REF_NAME_MINUS113);
5342
5343 err = got_ref_open(&target_ref, repo, target, 0);
5344 if (err)
5345 return err;
5346
5347 err = got_ref_alloc_symref(&ref, refname, target_ref);
5348 if (err)
5349 goto done;
5350
5351 err = got_ref_write(ref, repo);
5352done:
5353 if (target_ref)
5354 got_ref_close(target_ref);
5355 if (ref)
5356 got_ref_close(ref);
5357 return err;
5358}
5359
5360static const struct got_error *
5361cmd_ref(int argc, char *argv[])
5362{
5363 const struct got_error *error = NULL((void *)0);
5364 struct got_repository *repo = NULL((void *)0);
5365 struct got_worktree *worktree = NULL((void *)0);
5366 char *cwd = NULL((void *)0), *repo_path = NULL((void *)0);
5367 int ch, do_list = 0, do_delete = 0;
5368 const char *obj_arg = NULL((void *)0), *symref_target= NULL((void *)0);
5369 char *refname = NULL((void *)0);
5370
5371 while ((ch = getopt(argc, argv, "c:dr:ls:")) != -1) {
5372 switch (ch) {
5373 case 'c':
5374 obj_arg = optarg;
5375 break;
5376 case 'd':
5377 do_delete = 1;
5378 break;
5379 case 'r':
5380 repo_path = realpath(optarg, NULL((void *)0));
5381 if (repo_path == NULL((void *)0))
5382 return got_error_from_errno2("realpath",
5383 optarg);
5384 got_path_strip_trailing_slashes(repo_path);
5385 break;
5386 case 'l':
5387 do_list = 1;
5388 break;
5389 case 's':
5390 symref_target = optarg;
5391 break;
5392 default:
5393 usage_ref();
5394 /* NOTREACHED */
5395 }
5396 }
5397
5398 if (obj_arg && do_list)
5399 option_conflict('c', 'l');
5400 if (obj_arg && do_delete)
5401 option_conflict('c', 'd');
5402 if (obj_arg && symref_target)
5403 option_conflict('c', 's');
5404 if (symref_target && do_delete)
5405 option_conflict('s', 'd');
5406 if (symref_target && do_list)
5407 option_conflict('s', 'l');
5408 if (do_delete && do_list)
5409 option_conflict('d', 'l');
5410
5411 argc -= optind;
5412 argv += optind;
5413
5414 if (do_list) {
5415 if (argc != 0 && argc != 1)
5416 usage_ref();
5417 if (argc == 1) {
5418 refname = strdup(argv[0]);
5419 if (refname == NULL((void *)0)) {
5420 error = got_error_from_errno("strdup");
5421 goto done;
5422 }
5423 }
5424 } else {
5425 if (argc != 1)
5426 usage_ref();
5427 refname = strdup(argv[0]);
5428 if (refname == NULL((void *)0)) {
5429 error = got_error_from_errno("strdup");
5430 goto done;
5431 }
5432 }
5433
5434 if (refname)
5435 got_path_strip_trailing_slashes(refname);
5436
5437#ifndef PROFILE
5438 if (do_list) {
5439 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
5440 NULL((void *)0)) == -1)
5441 err(1, "pledge");
5442 } else {
5443 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
5444 "sendfd unveil", NULL((void *)0)) == -1)
5445 err(1, "pledge");
5446 }
5447#endif
5448 cwd = getcwd(NULL((void *)0), 0);
5449 if (cwd == NULL((void *)0)) {
5450 error = got_error_from_errno("getcwd");
5451 goto done;
5452 }
5453
5454 if (repo_path == NULL((void *)0)) {
5455 error = got_worktree_open(&worktree, cwd);
5456 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
5457 goto done;
5458 else
5459 error = NULL((void *)0);
5460 if (worktree) {
5461 repo_path =
5462 strdup(got_worktree_get_repo_path(worktree));
5463 if (repo_path == NULL((void *)0))
5464 error = got_error_from_errno("strdup");
5465 if (error)
5466 goto done;
5467 } else {
5468 repo_path = strdup(cwd);
5469 if (repo_path == NULL((void *)0)) {
5470 error = got_error_from_errno("strdup");
5471 goto done;
5472 }
5473 }
5474 }
5475
5476 error = got_repo_open(&repo, repo_path, NULL((void *)0));
5477 if (error != NULL((void *)0))
5478 goto done;
5479
5480 error = apply_unveil(got_repo_get_path(repo), do_list,
5481 worktree ? got_worktree_get_root_path(worktree) : NULL((void *)0));
5482 if (error)
5483 goto done;
5484
5485 if (do_list)
5486 error = list_refs(repo, refname);
5487 else if (do_delete)
5488 error = delete_ref(repo, refname);
5489 else if (symref_target)
5490 error = add_symref(repo, refname, symref_target);
5491 else {
5492 if (obj_arg == NULL((void *)0))
5493 usage_ref();
5494 error = add_ref(repo, refname, obj_arg);
5495 }
5496done:
5497 free(refname);
5498 if (repo)
5499 got_repo_close(repo);
5500 if (worktree)
5501 got_worktree_close(worktree);
5502 free(cwd);
5503 free(repo_path);
5504 return error;
5505}
5506
5507__dead__attribute__((__noreturn__)) static void
5508usage_branch(void)
5509{
5510 fprintf(stderr(&__sF[2]),
5511 "usage: %s branch [-c commit] [-d] [-r repository] [-l] [-n] "
5512 "[name]\n", getprogname());
5513 exit(1);
5514}
5515
5516static const struct got_error *
5517list_branch(struct got_repository *repo, struct got_worktree *worktree,
5518 struct got_reference *ref)
5519{
5520 const struct got_error *err = NULL((void *)0);
5521 const char *refname, *marker = " ";
5522 char *refstr;
5523
5524 refname = got_ref_get_name(ref);
5525 if (worktree && strcmp(refname,
5526 got_worktree_get_head_ref_name(worktree)) == 0) {
5527 struct got_object_id *id = NULL((void *)0);
5528
5529 err = got_ref_resolve(&id, repo, ref);
5530 if (err)
5531 return err;
5532 if (got_object_id_cmp(id,
5533 got_worktree_get_base_commit_id(worktree)) == 0)
5534 marker = "* ";
5535 else
5536 marker = "~ ";
5537 free(id);
5538 }
5539
5540 if (strncmp(refname, "refs/heads/", 11) == 0)
5541 refname += 11;
5542 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
5543 refname += 18;
5544 if (strncmp(refname, "refs/remotes/", 13) == 0)
5545 refname += 13;
5546
5547 refstr = got_ref_to_str(ref);
5548 if (refstr == NULL((void *)0))
5549 return got_error_from_errno("got_ref_to_str");
5550
5551 printf("%s%s: %s\n", marker, refname, refstr);
5552 free(refstr);
5553 return NULL((void *)0);
5554}
5555
5556static const struct got_error *
5557show_current_branch(struct got_repository *repo, struct got_worktree *worktree)
5558{
5559 const char *refname;
5560
5561 if (worktree == NULL((void *)0))
5562 return got_error(GOT_ERR_NOT_WORKTREE60);
5563
5564 refname = got_worktree_get_head_ref_name(worktree);
5565
5566 if (strncmp(refname, "refs/heads/", 11) == 0)
5567 refname += 11;
5568 if (strncmp(refname, "refs/got/worktree/", 18) == 0)
5569 refname += 18;
5570
5571 printf("%s\n", refname);
5572
5573 return NULL((void *)0);
5574}
5575
5576static const struct got_error *
5577list_branches(struct got_repository *repo, struct got_worktree *worktree)
5578{
5579 static const struct got_error *err = NULL((void *)0);
5580 struct got_reflist_head refs;
5581 struct got_reflist_entry *re;
5582 struct got_reference *temp_ref = NULL((void *)0);
5583 int rebase_in_progress, histedit_in_progress;
5584
5585 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
5586
5587 if (worktree) {
5588 err = got_worktree_rebase_in_progress(&rebase_in_progress,
5589 worktree);
5590 if (err)
5591 return err;
5592
5593 err = got_worktree_histedit_in_progress(&histedit_in_progress,
5594 worktree);
5595 if (err)
5596 return err;
5597
5598 if (rebase_in_progress || histedit_in_progress) {
5599 err = got_ref_open(&temp_ref, repo,
5600 got_worktree_get_head_ref_name(worktree), 0);
5601 if (err)
5602 return err;
5603 list_branch(repo, worktree, temp_ref);
5604 got_ref_close(temp_ref);
5605 }
5606 }
5607
5608 err = got_ref_list(&refs, repo, "refs/heads",
5609 got_ref_cmp_by_name, NULL((void *)0));
5610 if (err)
5611 return err;
5612
5613 TAILQ_FOREACH(re, &refs, entry)for((re) = ((&refs)->tqh_first); (re) != ((void *)0); (
re) = ((re)->entry.tqe_next))
5614 list_branch(repo, worktree, re->ref);
5615
5616 got_ref_list_free(&refs);
5617
5618 err = got_ref_list(&refs, repo, "refs/remotes",
5619 got_ref_cmp_by_name, NULL((void *)0));
5620 if (err)
5621 return err;
5622
5623 TAILQ_FOREACH(re, &refs, entry)for((re) = ((&refs)->tqh_first); (re) != ((void *)0); (
re) = ((re)->entry.tqe_next))
5624 list_branch(repo, worktree, re->ref);
5625
5626 got_ref_list_free(&refs);
5627
5628 return NULL((void *)0);
5629}
5630
5631static const struct got_error *
5632delete_branch(struct got_repository *repo, struct got_worktree *worktree,
5633 const char *branch_name)
5634{
5635 const struct got_error *err = NULL((void *)0);
5636 struct got_reference *ref = NULL((void *)0);
5637 char *refname;
5638
5639 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1)
5640 return got_error_from_errno("asprintf");
5641
5642 err = got_ref_open(&ref, repo, refname, 0);
5643 if (err)
5644 goto done;
5645
5646 if (worktree &&
5647 strcmp(got_worktree_get_head_ref_name(worktree),
5648 got_ref_get_name(ref)) == 0) {
5649 err = got_error_msg(GOT_ERR_SAME_BRANCH79,
5650 "will not delete this work tree's current branch");
5651 goto done;
5652 }
5653
5654 err = got_ref_delete(ref, repo);
5655done:
5656 if (ref)
5657 got_ref_close(ref);
5658 free(refname);
5659 return err;
5660}
5661
5662static const struct got_error *
5663add_branch(struct got_repository *repo, const char *branch_name,
5664 struct got_object_id *base_commit_id)
5665{
5666 const struct got_error *err = NULL((void *)0);
5667 struct got_reference *ref = NULL((void *)0);
5668 char *base_refname = NULL((void *)0), *refname = NULL((void *)0);
5669
5670 /*
5671 * Don't let the user create a branch name with a leading '-'.
5672 * While technically a valid reference name, this case is usually
5673 * an unintended typo.
5674 */
5675 if (branch_name[0] == '-')
5676 return got_error_path(branch_name, GOT_ERR_REF_NAME_MINUS113);
5677
5678 if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
5679 err = got_error_from_errno("asprintf");
5680 goto done;
5681 }
5682
5683 err = got_ref_open(&ref, repo, refname, 0);
5684 if (err == NULL((void *)0)) {
5685 err = got_error(GOT_ERR_BRANCH_EXISTS83);
5686 goto done;
5687 } else if (err->code != GOT_ERR_NOT_REF5)
5688 goto done;
5689
5690 err = got_ref_alloc(&ref, refname, base_commit_id);
5691 if (err)
5692 goto done;
5693
5694 err = got_ref_write(ref, repo);
5695done:
5696 if (ref)
5697 got_ref_close(ref);
5698 free(base_refname);
5699 free(refname);
5700 return err;
5701}
5702
5703static const struct got_error *
5704cmd_branch(int argc, char *argv[])
5705{
5706 const struct got_error *error = NULL((void *)0);
5707 struct got_repository *repo = NULL((void *)0);
5708 struct got_worktree *worktree = NULL((void *)0);
5709 char *cwd = NULL((void *)0), *repo_path = NULL((void *)0);
5710 int ch, do_list = 0, do_show = 0, do_update = 1;
5711 const char *delref = NULL((void *)0), *commit_id_arg = NULL((void *)0);
5712 struct got_reference *ref = NULL((void *)0);
5713 struct got_pathlist_head paths;
5714 struct got_pathlist_entry *pe;
5715 struct got_object_id *commit_id = NULL((void *)0);
5716 char *commit_id_str = NULL((void *)0);
5717
5718 TAILQ_INIT(&paths)do { (&paths)->tqh_first = ((void *)0); (&paths)->
tqh_last = &(&paths)->tqh_first; } while (0)
;
5719
5720 while ((ch = getopt(argc, argv, "c:d:r:ln")) != -1) {
5721 switch (ch) {
5722 case 'c':
5723 commit_id_arg = optarg;
5724 break;
5725 case 'd':
5726 delref = optarg;
5727 break;
5728 case 'r':
5729 repo_path = realpath(optarg, NULL((void *)0));
5730 if (repo_path == NULL((void *)0))
5731 return got_error_from_errno2("realpath",
5732 optarg);
5733 got_path_strip_trailing_slashes(repo_path);
5734 break;
5735 case 'l':
5736 do_list = 1;
5737 break;
5738 case 'n':
5739 do_update = 0;
5740 break;
5741 default:
5742 usage_branch();
5743 /* NOTREACHED */
5744 }
5745 }
5746
5747 if (do_list && delref)
5748 option_conflict('l', 'd');
5749
5750 argc -= optind;
5751 argv += optind;
5752
5753 if (!do_list && !delref && argc == 0)
5754 do_show = 1;
5755
5756 if ((do_list || delref || do_show) && commit_id_arg != NULL((void *)0))
5757 errx(1, "-c option can only be used when creating a branch");
5758
5759 if (do_list || delref) {
5760 if (argc > 0)
5761 usage_branch();
5762 } else if (!do_show && argc != 1)
5763 usage_branch();
5764
5765#ifndef PROFILE
5766 if (do_list || do_show) {
5767 if (pledge("stdio rpath wpath flock proc exec sendfd unveil",
5768 NULL((void *)0)) == -1)
5769 err(1, "pledge");
5770 } else {
5771 if (pledge("stdio rpath wpath cpath fattr flock proc exec "
5772 "sendfd unveil", NULL((void *)0)) == -1)
5773 err(1, "pledge");
5774 }
5775#endif
5776 cwd = getcwd(NULL((void *)0), 0);
5777 if (cwd == NULL((void *)0)) {
5778 error = got_error_from_errno("getcwd");
5779 goto done;
5780 }
5781
5782 if (repo_path == NULL((void *)0)) {
5783 error = got_worktree_open(&worktree, cwd);
5784 if (error && error->code != GOT_ERR_NOT_WORKTREE60)
5785 goto done;
5786 else
5787 error = NULL((void *)0);
5788 if (worktree) {
5789 repo_path =
5790 strdup(got_worktree_get_repo_path(worktree));
5791 if (repo_path == NULL((void *)0))
5792 error = got_error_from_errno("strdup");
5793 if (error)
5794 goto done;
5795 } else {
5796 repo_path = strdup(cwd);
5797 if (repo_path == NULL((void *)0)) {
5798 error = got_error_from_errno("strdup");
5799 goto done;
5800 }
5801 }
5802 }
5803
5804 error = got_repo_open(&repo, repo_path, NULL((void *)0));
5805 if (error != NULL((void *)0))
5806 goto done;
5807
5808 error = apply_unveil(got_repo_get_path(repo), do_list,
5809 worktree ? got_worktree_get_root_path(worktree) : NULL((void *)0));
5810 if (error)
5811 goto done;
5812
5813 if (do_show)
5814 error = show_current_branch(repo, worktree);
5815 else if (do_list)
5816 error = list_branches(repo, worktree);
5817 else if (delref)
5818 error = delete_branch(repo, worktree, delref);
5819 else {
5820 struct got_reflist_head refs;
5821 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
5822 error = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name,
5823 NULL((void *)0));
5824 if (error)
5825 goto done;
5826 if (commit_id_arg == NULL((void *)0))
5827 commit_id_arg = worktree ?
5828 got_worktree_get_head_ref_name(worktree) :
5829 GOT_REF_HEAD"HEAD";
5830 error = got_repo_match_object_id(&commit_id, NULL((void *)0),
5831 commit_id_arg, GOT_OBJ_TYPE_COMMIT1, &refs, repo);
5832 got_ref_list_free(&refs);
5833 if (error)
5834 goto done;
5835 error = add_branch(repo, argv[0], commit_id);
5836 if (error)
5837 goto done;
5838 if (worktree && do_update) {
5839 struct got_update_progress_arg upa;
5840 char *branch_refname = NULL((void *)0);
5841
5842 error = got_object_id_str(&commit_id_str, commit_id);
5843 if (error)
5844 goto done;
5845 error = get_worktree_paths_from_argv(&paths, 0, NULL((void *)0),
5846 worktree);
5847 if (error)
5848 goto done;
5849 if (asprintf(&branch_refname, "refs/heads/%s", argv[0])
5850 == -1) {
5851 error = got_error_from_errno("asprintf");
5852 goto done;
5853 }
5854 error = got_ref_open(&ref, repo, branch_refname, 0);
5855 free(branch_refname);
5856 if (error)
5857 goto done;
5858 error = switch_head_ref(ref, commit_id, worktree,
5859 repo);
5860 if (error)
5861 goto done;
5862 error = got_worktree_set_base_commit_id(worktree, repo,
5863 commit_id);
5864 if (error)
5865 goto done;
5866 memset(&upa, 0, sizeof(upa));
5867 error = got_worktree_checkout_files(worktree, &paths,
5868 repo, update_progress, &upa, check_cancelled,
5869 NULL((void *)0));
5870 if (error)
5871 goto done;
5872 if (upa.did_something)
5873 printf("Updated to commit %s\n", commit_id_str);
5874 print_update_progress_stats(&upa);
5875 }
5876 }
5877done:
5878 if (ref)
5879 got_ref_close(ref);
5880 if (repo)
5881 got_repo_close(repo);
5882 if (worktree)
5883 got_worktree_close(worktree);
5884 free(cwd);
5885 free(repo_path);
5886 free(commit_id);
5887 free(commit_id_str);
5888 TAILQ_FOREACH(pe, &paths, entry)for((pe) = ((&paths)->tqh_first); (pe) != ((void *)0);
(pe) = ((pe)->entry.tqe_next))
5889 free((char *)pe->path);
5890 got_pathlist_free(&paths);
5891 return error;
5892}
5893
5894
5895__dead__attribute__((__noreturn__)) static void
5896usage_tag(void)
5897{
5898 fprintf(stderr(&__sF[2]),
5899 "usage: %s tag [-c commit] [-r repository] [-l] "
5900 "[-m message] name\n", getprogname());
5901 exit(1);
5902}
5903
5904#if 0
5905static const struct got_error *
5906sort_tags(struct got_reflist_head *sorted, struct got_reflist_head *tags)
5907{
5908 const struct got_error *err = NULL((void *)0);
5909 struct got_reflist_entry *re, *se, *new;
5910 struct got_object_id *re_id, *se_id;
5911 struct got_tag_object *re_tag, *se_tag;
5912 time_t re_time, se_time;
5913
5914 SIMPLEQ_FOREACH(re, tags, entry)for((re) = ((tags)->sqh_first); (re) != ((void *)0); (re) =
((re)->entry.sqe_next))
{
5915 se = SIMPLEQ_FIRST(sorted)((sorted)->sqh_first);
5916 if (se == NULL((void *)0)) {
5917 err = got_reflist_entry_dup(&new, re);
5918 if (err)
5919 return err;
5920 SIMPLEQ_INSERT_HEAD(sorted, new, entry)do { if (((new)->entry.sqe_next = (sorted)->sqh_first) ==
((void *)0)) (sorted)->sqh_last = &(new)->entry.sqe_next
; (sorted)->sqh_first = (new); } while (0)
;
5921 continue;
5922 } else {
5923 err = got_ref_resolve(&re_id, repo, re->ref);
5924 if (err)
5925 break;
5926 err = got_object_open_as_tag(&re_tag, repo, re_id);
5927 free(re_id);
5928 if (err)
5929 break;
5930 re_time = got_object_tag_get_tagger_time(re_tag);
5931 got_object_tag_close(re_tag);
5932 }
5933
5934 while (se) {
5935 err = got_ref_resolve(&se_id, repo, re->ref);
5936 if (err)
5937 break;
5938 err = got_object_open_as_tag(&se_tag, repo, se_id);
5939 free(se_id);
5940 if (err)
5941 break;
5942 se_time = got_object_tag_get_tagger_time(se_tag);
5943 got_object_tag_close(se_tag);
5944
5945 if (se_time > re_time) {
5946 err = got_reflist_entry_dup(&new, re);
5947 if (err)
5948 return err;
5949 SIMPLEQ_INSERT_AFTER(sorted, se, new, entry)do { if (((new)->entry.sqe_next = (se)->entry.sqe_next)
== ((void *)0)) (sorted)->sqh_last = &(new)->entry
.sqe_next; (se)->entry.sqe_next = (new); } while (0)
;
5950 break;
5951 }
5952 se = SIMPLEQ_NEXT(se, entry)((se)->entry.sqe_next);
5953 continue;
5954 }
5955 }
5956done:
5957 return err;
5958}
5959#endif
5960
5961static const struct got_error *
5962list_tags(struct got_repository *repo, struct got_worktree *worktree)
5963{
5964 static const struct got_error *err = NULL((void *)0);
5965 struct got_reflist_head refs;
5966 struct got_reflist_entry *re;
5967
5968 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
5969
5970 err = got_ref_list(&refs, repo, "refs/tags", got_ref_cmp_tags, repo);
5971 if (err)
5972 return err;
5973
5974 TAILQ_FOREACH(re, &refs, entry)for((re) = ((&refs)->tqh_first); (re) != ((void *)0); (
re) = ((re)->entry.tqe_next))
{
5975 const char *refname;
5976 char *refstr, *tagmsg0, *tagmsg, *line, *id_str, *datestr;
5977 char datebuf[26];
5978 const char *tagger;
5979 time_t tagger_time;
5980 struct got_object_id *id;
5981 struct got_tag_object *tag;
5982 struct got_commit_object *commit = NULL((void *)0);
5983
5984 refname = got_ref_get_name(re->ref);
5985 if (strncmp(refname, "refs/tags/", 10) != 0)
5986 continue;
5987 refname += 10;
5988 refstr = got_ref_to_str(re->ref);
5989 if (refstr == NULL((void *)0)) {
5990 err = got_error_from_errno("got_ref_to_str");
5991 break;
5992 }
5993 printf("%stag %s %s\n", GOT_COMMIT_SEP_STR"-----------------------------------------------\n", refname, refstr);
5994 free(refstr);
5995
5996 err = got_ref_resolve(&id, repo, re->ref);
5997 if (err)
5998 break;
5999 err = got_object_open_as_tag(&tag, repo, id);
6000 if (err) {
6001 if (err->code != GOT_ERR_OBJ_TYPE11) {
6002 free(id);
6003 break;
6004 }
6005 /* "lightweight" tag */
6006 err = got_object_open_as_commit(&commit, repo, id);
6007 if (err) {
6008 free(id);
6009 break;
6010 }
6011 tagger = got_object_commit_get_committer(commit);
6012 tagger_time =
6013 got_object_commit_get_committer_time(commit);
6014 err = got_object_id_str(&id_str, id);
6015 free(id);
6016 if (err)
6017 break;
6018 } else {
6019 free(id);
6020 tagger = got_object_tag_get_tagger(tag);
6021 tagger_time = got_object_tag_get_tagger_time(tag);
6022 err = got_object_id_str(&id_str,
6023 got_object_tag_get_object_id(tag));
6024 if (err)
6025 break;
6026 }
6027 printf("from: %s\n", tagger);
6028 datestr = get_datestr(&tagger_time, datebuf);
6029 if (datestr)
6030 printf("date: %s UTC\n", datestr);
6031 if (commit)
6032 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT"commit", id_str);
6033 else {
6034 switch (got_object_tag_get_object_type(tag)) {
6035 case GOT_OBJ_TYPE_BLOB3:
6036 printf("object: %s %s\n", GOT_OBJ_LABEL_BLOB"blob",
6037 id_str);
6038 break;
6039 case GOT_OBJ_TYPE_TREE2:
6040 printf("object: %s %s\n", GOT_OBJ_LABEL_TREE"tree",
6041 id_str);
6042 break;
6043 case GOT_OBJ_TYPE_COMMIT1:
6044 printf("object: %s %s\n", GOT_OBJ_LABEL_COMMIT"commit",
6045 id_str);
6046 break;
6047 case GOT_OBJ_TYPE_TAG4:
6048 printf("object: %s %s\n", GOT_OBJ_LABEL_TAG"tag",
6049 id_str);
6050 break;
6051 default:
6052 break;
6053 }
6054 }
6055 free(id_str);
6056 if (commit) {
6057 err = got_object_commit_get_logmsg(&tagmsg0, commit);
6058 if (err)
6059 break;
6060 got_object_commit_close(commit);
6061 } else {
6062 tagmsg0 = strdup(got_object_tag_get_message(tag));
6063 got_object_tag_close(tag);
6064 if (tagmsg0 == NULL((void *)0)) {
6065 err = got_error_from_errno("strdup");
6066 break;
6067 }
6068 }
6069
6070 tagmsg = tagmsg0;
6071 do {
6072 line = strsep(&tagmsg, "\n");
6073 if (line)
6074 printf(" %s\n", line);
6075 } while (line);
6076 free(tagmsg0);
6077 }
6078
6079 got_ref_list_free(&refs);
6080 return NULL((void *)0);
6081}
6082
6083static const struct got_error *
6084get_tag_message(char **tagmsg, char **tagmsg_path, const char *commit_id_str,
6085 const char *tag_name, const char *repo_path)
6086{
6087 const struct got_error *err = NULL((void *)0);
6088 char *template = NULL((void *)0), *initial_content = NULL((void *)0);
6089 char *editor = NULL((void *)0);
6090 int initial_content_len;
6091 int fd = -1;
6092
6093 if (asprintf(&template, GOT_TMPDIR_STR"/tmp" "/got-tagmsg") == -1) {
6094 err = got_error_from_errno("asprintf");
6095 goto done;
6096 }
6097
6098 initial_content_len = asprintf(&initial_content,
6099 "\n# tagging commit %s as %s\n",
6100 commit_id_str, tag_name);
6101 if (initial_content_len == -1) {
6102 err = got_error_from_errno("asprintf");
6103 goto done;
6104 }
6105
6106 err = got_opentemp_named_fd(tagmsg_path, &fd, template);
6107 if (err)
6108 goto done;
6109
6110 if (write(fd, initial_content, initial_content_len) == -1) {
6111 err = got_error_from_errno2("write", *tagmsg_path);
6112 goto done;
6113 }
6114
6115 err = get_editor(&editor);
6116 if (err)
6117 goto done;
6118 err = edit_logmsg(tagmsg, editor, *tagmsg_path, initial_content,
6119 initial_content_len, 1);
6120done:
6121 free(initial_content);
6122 free(template);
6123 free(editor);
6124
6125 if (fd != -1 && close(fd) == -1 && err == NULL((void *)0))
6126 err = got_error_from_errno2("close", *tagmsg_path);
6127
6128 /* Editor is done; we can now apply unveil(2) */
6129 if (err == NULL((void *)0))
6130 err = apply_unveil(repo_path, 0, NULL((void *)0));
6131 if (err) {
6132 free(*tagmsg);
6133 *tagmsg = NULL((void *)0);
6134 }
6135 return err;
6136}
6137
6138static const struct got_error *
6139add_tag(struct got_repository *repo, struct got_worktree *worktree,
6140 const char *tag_name, const char *commit_arg, const char *tagmsg_arg)
6141{
6142 const struct got_error *err = NULL((void *)0);
6143 struct got_object_id *commit_id = NULL((void *)0), *tag_id = NULL((void *)0);
6144 char *label = NULL((void *)0), *commit_id_str = NULL((void *)0);
6145 struct got_reference *ref = NULL((void *)0);
6146 char *refname = NULL((void *)0), *tagmsg = NULL((void *)0), *tagger = NULL((void *)0);
6147 char *tagmsg_path = NULL((void *)0), *tag_id_str = NULL((void *)0);
6148 int preserve_tagmsg = 0;
6149 struct got_reflist_head refs;
6150
6151 TAILQ_INIT(&refs)do { (&refs)->tqh_first = ((void *)0); (&refs)->
tqh_last = &(&refs)->tqh_first; } while (0)
;
6152
6153 /*
6154 * Don't let the user create a tag name with a leading '-'.
6155 * While technically a valid reference name, this case is usually
6156 * an unintended typo.
6157 */
6158 if (tag_name[0] == '-')
6159 return got_error_path(tag_name, GOT_ERR_REF_NAME_MINUS113);
6160
6161 err = get_author(&tagger, repo, worktree);
6162 if (err)
6163 return err;
6164
6165 err = got_ref_list(&refs, repo, NULL((void *)0), got_ref_cmp_by_name, NULL((void *)0));
6166 if (err)
6167 goto done;
6168
6169 err = got_repo_match_object_id(&commit_id, &label, commit_arg,
6170 GOT_OBJ_TYPE_COMMIT1, &refs, repo);
6171 if (err)
6172 goto done;
6173
6174 err = got_object_id_str(&commit_id_str, commit_id);
6175 if (err)
6176 goto done;
6177
6178 if (strncmp("refs/tags/", tag_name, 10) == 0) {
6179 refname = strdup(tag_name);
6180 if (refname == NULL((void *)0)) {
6181 err = got_error_from_errno("strdup");
6182 goto done;
6183 }
6184 tag_name += 10;
6185 } else if (asprintf(&refname, "refs/tags/%s", tag_name) == -1) {
6186 err = got_error_from_errno("asprintf");
6187 goto done;
6188 }
6189
6190 err = got_ref_open(&ref, repo, refname, 0);
6191 if (err == NULL((void *)0)) {
6192 err = got_error(GOT_ERR_TAG_EXISTS109);
6193 goto done;
6194 } else if (err->code != GOT_ERR_NOT_REF5)
6195 goto done;
6196
6197 if (tagmsg_arg == NULL((void *)0)) {
6198 err = get_tag_message(&tagmsg, &tagmsg_path, commit_id_str,
6199 tag_name, got_repo_get_path(repo));
6200 if (er