commit - 43012d5817071b74fc38aef617a3eb8f2da4a945
commit + 3ce1b84566a5dc6cbbfcfc87507aa84de4f0c9b9
blob - ac8037722555366872b56f211bcccace894db4d8
blob + 1103aa277a4635a8fe84c1c7112afb0056d03ded
--- got/got.1
+++ got/got.1
.It Cm init Ar path
Create a new empty repository at the specified
.Ar path .
+.Pp
+After
+.Cm got init ,
+the
+.Cm got import
+command must be used to populate the empty repository before
+.Cm got checkout
+can be used.
+.Pp
+.It Cm import [ Fl b Ar branch ] [ Fl m Ar message ] [ Fl r Ar repository-path ] [ Fl I Ar pattern ] directory
+Create an initial commit in a repository from the file hierarchy
+within the specified
+.Ar directory .
+The created commit will not have any parent commits, i.e. it will be a
+root commit.
+Also create a new reference which provides a branch name for the newly
+created commit.
+.Pp
+Show the path of each added file to indicate progress.
+.Pp
+The options for
+.Cm got import
+are as follows:
+.Bl -tag -width Ds
+.It Fl b Ar branch
+Create the specified
+.Ar branch
+instead of creating the default branch
+.Dq master .
+Use of this option is required if the
+.Dq master
+branch already exists.
+.It Fl m Ar message
+Use the specified log message when creating the new commit.
+Without the
+.Fl m
+option,
+.Cm got import
+opens a temporary file in an editor where a log message can be written.
+.It Fl r Ar repository-path
+Use the repository at the specified path.
+If not specified, assume the repository is located at or above the current
+working directory.
+.It Fl I Ar pattern
+Ignore files or directories with a name which matches the specified
+.Ar pattern .
+This option may be specified multiple times to build a list of ignore patterns.
+The
+.Ar pattern
+follows the globbing rules documented in
+.Xr glob 7 .
+.El
.It Cm checkout [ Fl b Ar branch ] [ Fl c Ar commit ] [ Fl p Ar path-prefix ] repository-path [ work-tree-path ]
Copy files from a repository into a new work tree.
If the
.Sh EXIT STATUS
.Ex -std got
.Sh EXAMPLES
-.Pp
Clone an existing Git repository for use with
.Nm .
This step currently requires
.Dl $ cd /var/git/
.Dl $ git clone --bare https://github.com/openbsd/src.git
.Pp
-Check out a work tree from this Git repository to /usr/src:
+Alternatively, for quick and dirty local testing of
+.Nm
+a new Git repository could be created and populated with files,
+e.g. from a temporary CVS checkout located at
+.Pa /tmp/src :
.Pp
+.Dl $ got init /var/git/src.git
+.Dl $ got import -r /var/src.git -I CVS -I obj /tmp/src
+.Pp
+Check out a work tree from the Git repository to /usr/src:
+.Pp
.Dl $ got checkout /var/git/src.git /usr/src
.Pp
View local changes in a work tree directory:
blob - 0bfd597accf4b09dfa3552fc715ec1c20c3108b7
blob + 2f7d8b5ef12121c16e9340c8363bb580459f61da
--- got/got.c
+++ got/got.c
__dead static void usage(int);
__dead static void usage_init(void);
+__dead static void usage_import(void);
__dead static void usage_checkout(void);
__dead static void usage_update(void);
__dead static void usage_log(void);
__dead static void usage_rebase(void);
static const struct got_error* cmd_init(int, char *[]);
+static const struct got_error* cmd_import(int, char *[]);
static const struct got_error* cmd_checkout(int, char *[]);
static const struct got_error* cmd_update(int, char *[]);
static const struct got_error* cmd_log(int, char *[]);
static struct got_cmd got_commands[] = {
{ "init", cmd_init, usage_init, "" },
+ { "import", cmd_import, usage_import, "" },
{ "checkout", cmd_checkout, usage_checkout, "co" },
{ "update", cmd_update, usage_update, "up" },
{ "log", cmd_log, usage_log, "" },
error = got_repo_init(repo_path);
if (error != NULL)
+ goto done;
+
+done:
+ free(repo_path);
+ return error;
+}
+
+__dead static void
+usage_import(void)
+{
+ fprintf(stderr, "usage: %s import [-b branch] [-m message] "
+ "[-r repository-path] [-I pattern] path\n", getprogname());
+ exit(1);
+}
+
+int
+spawn_editor(const char *editor, const char *file)
+{
+ pid_t pid;
+ sig_t sighup, sigint, sigquit;
+ int st = -1;
+
+ sighup = signal(SIGHUP, SIG_IGN);
+ sigint = signal(SIGINT, SIG_IGN);
+ sigquit = signal(SIGQUIT, SIG_IGN);
+
+ switch (pid = fork()) {
+ case -1:
+ goto doneediting;
+ case 0:
+ execl(editor, editor, file, (char *)NULL);
+ _exit(127);
+ }
+
+ while (waitpid(pid, &st, 0) == -1)
+ if (errno != EINTR)
+ break;
+
+doneediting:
+ (void)signal(SIGHUP, sighup);
+ (void)signal(SIGINT, sigint);
+ (void)signal(SIGQUIT, sigquit);
+
+ if (!WIFEXITED(st)) {
+ errno = EINTR;
+ return -1;
+ }
+
+ return WEXITSTATUS(st);
+}
+
+static const struct got_error *
+edit_logmsg(char **logmsg, const char *editor, const char *logmsg_path,
+ const char *initial_content)
+{
+ const struct got_error *err = NULL;
+ char buf[1024];
+ struct stat st, st2;
+ FILE *fp;
+ int content_changed = 0;
+ size_t len;
+
+ *logmsg = NULL;
+
+ if (stat(logmsg_path, &st) == -1)
+ return got_error_from_errno2("stat", logmsg_path);
+
+ if (spawn_editor(editor, logmsg_path) == -1)
+ return got_error_from_errno("failed spawning editor");
+
+ if (stat(logmsg_path, &st2) == -1)
+ return got_error_from_errno("stat");
+
+ if (st.st_mtime == st2.st_mtime && st.st_size == st2.st_size)
+ return got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
+ "no changes made to commit message, aborting");
+
+ *logmsg = malloc(st2.st_size + 1);
+ if (*logmsg == NULL)
+ return got_error_from_errno("malloc");
+ (*logmsg)[0] = '\0';
+ len = 0;
+
+ fp = fopen(logmsg_path, "r");
+ if (fp == NULL) {
+ err = got_error_from_errno("fopen");
+ goto done;
+ }
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ if (!content_changed && strcmp(buf, initial_content) != 0)
+ content_changed = 1;
+ if (buf[0] == '#' || (len == 0 && buf[0] == '\n'))
+ continue; /* remove comments and leading empty lines */
+ len = strlcat(*logmsg, buf, st2.st_size);
+ }
+ fclose(fp);
+
+ while (len > 0 && (*logmsg)[len - 1] == '\n') {
+ (*logmsg)[len - 1] = '\0';
+ len--;
+ }
+
+ if (len == 0 || !content_changed)
+ err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
+ "commit message cannot be empty, aborting");
+done:
+ if (err) {
+ free(*logmsg);
+ *logmsg = NULL;
+ }
+ return err;
+}
+
+static const struct got_error *
+collect_import_msg(char **logmsg, const char *editor, const char *path_dir,
+ const char *branch_name)
+{
+ char *initial_content = NULL, *logmsg_path = NULL;
+ const struct got_error *err = NULL;
+ int fd;
+
+ if (asprintf(&initial_content,
+ "\n# %s to be imported to branch %s\n", path_dir,
+ branch_name) == -1)
+ return got_error_from_errno("asprintf");
+
+ err = got_opentemp_named_fd(&logmsg_path, &fd, "/tmp/got-importmsg");
+ if (err)
+ goto done;
+
+ dprintf(fd, initial_content);
+ close(fd);
+
+ err = edit_logmsg(logmsg, editor, logmsg_path, initial_content);
+done:
+ free(initial_content);
+ free(logmsg_path);
+ return err;
+}
+
+static const struct got_error *
+import_progress(void *arg, const char *path)
+{
+ printf("A %s\n", path);
+ return NULL;
+}
+
+static const struct got_error *
+cmd_import(int argc, char *argv[])
+{
+ const struct got_error *error = NULL;
+ char *path_dir = NULL, *repo_path = NULL, *logmsg = NULL;
+ char *editor = NULL;
+ const char *got_author = getenv("GOT_AUTHOR");
+ const char *branch_name = "master";
+ char *refname = NULL, *id_str = NULL;
+ struct got_repository *repo = NULL;
+ struct got_reference *branch_ref = NULL, *head_ref = NULL;
+ struct got_object_id *new_commit_id = NULL;
+ int ch;
+ struct got_pathlist_head ignores;
+ struct got_pathlist_entry *pe;
+
+ TAILQ_INIT(&ignores);
+
+ while ((ch = getopt(argc, argv, "b:m:r:I:")) != -1) {
+ switch (ch) {
+ case 'b':
+ branch_name = optarg;
+ break;
+ case 'm':
+ logmsg = strdup(optarg);
+ if (logmsg == NULL) {
+ error = got_error_from_errno("strdup");
+ goto done;
+ }
+ break;
+ case 'r':
+ repo_path = realpath(optarg, NULL);
+ if (repo_path == NULL) {
+ error = got_error_from_errno("realpath");
+ goto done;
+ }
+ break;
+ case 'I':
+ if (optarg[0] == '\0')
+ break;
+ error = got_pathlist_insert(&pe, &ignores, optarg,
+ NULL);
+ if (error)
+ goto done;
+ break;
+ default:
+ usage_init();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+#ifndef PROFILE
+ if (pledge("stdio rpath wpath cpath fattr flock proc exec unveil",
+ NULL) == -1)
+ err(1, "pledge");
+#endif
+ if (argc != 1)
+ usage_import();
+
+ if (got_author == NULL) {
+ /* TODO: Look current user up in password database */
+ error = got_error(GOT_ERR_COMMIT_NO_AUTHOR);
goto done;
+ }
+ if (repo_path == NULL) {
+ repo_path = getcwd(NULL, 0);
+ if (repo_path == NULL)
+ return got_error_from_errno("getcwd");
+ }
+ got_path_strip_trailing_slashes(repo_path);
+ error = got_repo_open(&repo, repo_path);
+ if (error)
+ goto done;
+
+ if (asprintf(&refname, "refs/heads/%s", branch_name) == -1) {
+ error = got_error_from_errno("asprintf");
+ goto done;
+ }
+
+ error = got_ref_open(&branch_ref, repo, refname, 0);
+ if (error) {
+ if (error->code != GOT_ERR_NOT_REF)
+ goto done;
+ } else {
+ error = got_error_msg(GOT_ERR_BRANCH_EXISTS,
+ "import target branch already exists");
+ goto done;
+ }
+
+ path_dir = realpath(argv[0], NULL);
+ if (path_dir == NULL) {
+ error = got_error_from_errno("realpath");
+ goto done;
+ }
+ got_path_strip_trailing_slashes(path_dir);
+
+ /*
+ * unveil(2) traverses exec(2); if an editor is used we have
+ * to apply unveil after the log message has been written.
+ */
+ if (logmsg == NULL || strlen(logmsg) == 0) {
+ error = get_editor(&editor);
+ if (error)
+ goto done;
+ error = collect_import_msg(&logmsg, editor, path_dir, refname);
+ if (error)
+ goto done;
+ }
+
+ if (unveil(path_dir, "r") != 0)
+ return got_error_from_errno2("unveil", path_dir);
+
+ error = apply_unveil(got_repo_get_path(repo), 0, NULL, 0);
+ if (error)
+ goto done;
+
+ error = got_repo_import(&new_commit_id, path_dir, logmsg,
+ got_author, &ignores, repo, import_progress, NULL);
+ if (error)
+ goto done;
+
+ error = got_ref_alloc(&branch_ref, refname, new_commit_id);
+ if (error)
+ goto done;
+
+ error = got_ref_write(branch_ref, repo);
+ if (error)
+ goto done;
+
+ error = got_object_id_str(&id_str, new_commit_id);
+ if (error)
+ goto done;
+
+ error = got_ref_open(&head_ref, repo, GOT_REF_HEAD, 0);
+ if (error) {
+ if (error->code != GOT_ERR_NOT_REF)
+ goto done;
+
+ error = got_ref_alloc_symref(&head_ref, GOT_REF_HEAD,
+ branch_ref);
+ if (error)
+ goto done;
+
+ error = got_ref_write(head_ref, repo);
+ if (error)
+ goto done;
+ }
+
+ printf("Created branch %s with commit %s\n",
+ got_ref_get_name(branch_ref), id_str);
done:
free(repo_path);
+ free(editor);
+ free(refname);
+ free(new_commit_id);
+ free(id_str);
+ if (branch_ref)
+ got_ref_close(branch_ref);
+ if (head_ref)
+ got_ref_close(head_ref);
return error;
}
fprintf(stderr, "usage: %s commit [-m msg] file-path\n", getprogname());
exit(1);
}
-
-int
-spawn_editor(const char *editor, const char *file)
-{
- pid_t pid;
- sig_t sighup, sigint, sigquit;
- int st = -1;
- sighup = signal(SIGHUP, SIG_IGN);
- sigint = signal(SIGINT, SIG_IGN);
- sigquit = signal(SIGQUIT, SIG_IGN);
-
- switch (pid = fork()) {
- case -1:
- goto doneediting;
- case 0:
- execl(editor, editor, file, (char *)NULL);
- _exit(127);
- }
-
- while (waitpid(pid, &st, 0) == -1)
- if (errno != EINTR)
- break;
-
-doneediting:
- (void)signal(SIGHUP, sighup);
- (void)signal(SIGINT, sigint);
- (void)signal(SIGQUIT, sigquit);
-
- if (!WIFEXITED(st)) {
- errno = EINTR;
- return -1;
- }
-
- return WEXITSTATUS(st);
-}
-
struct collect_commit_logmsg_arg {
const char *cmdline_log;
const char *editor;
const struct got_error *err = NULL;
char *template = NULL;
struct collect_commit_logmsg_arg *a = arg;
- char buf[1024];
- struct stat st, st2;
- FILE *fp;
+ int fd;
size_t len;
- int fd, content_changed = 0;
/* if a message was specified on the command line, just use it */
if (a->cmdline_log != NULL && strlen(a->cmdline_log) != 0) {
}
close(fd);
- if (stat(a->logmsg_path, &st) == -1) {
- err = got_error_from_errno2("stat", a->logmsg_path);
- goto done;
- }
-
- if (spawn_editor(a->editor, a->logmsg_path) == -1) {
- err = got_error_from_errno("failed spawning editor");
- goto done;
- }
-
- if (stat(a->logmsg_path, &st2) == -1) {
- err = got_error_from_errno("stat");
- goto done;
- }
-
- if (st.st_mtime == st2.st_mtime && st.st_size == st2.st_size) {
- unlink(a->logmsg_path);
- free(a->logmsg_path);
- a->logmsg_path = NULL;
- err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
- "no changes made to commit message, aborting");
- goto done;
- }
-
- *logmsg = malloc(st2.st_size + 1);
- if (*logmsg == NULL) {
- err = got_error_from_errno("malloc");
- goto done;
- }
- (*logmsg)[0] = '\0';
- len = 0;
-
- fp = fopen(a->logmsg_path, "r");
- if (fp == NULL) {
- err = got_error_from_errno("fopen");
- goto done;
- }
- while (fgets(buf, sizeof(buf), fp) != NULL) {
- if (!content_changed && strcmp(buf, initial_content) != 0)
- content_changed = 1;
- if (buf[0] == '#' || (len == 0 && buf[0] == '\n'))
- continue; /* remove comments and leading empty lines */
- len = strlcat(*logmsg, buf, st2.st_size);
- }
- fclose(fp);
-
- while (len > 0 && (*logmsg)[len - 1] == '\n') {
- (*logmsg)[len - 1] = '\0';
- len--;
- }
-
- if (len == 0 || !content_changed) {
- unlink(a->logmsg_path);
- free(a->logmsg_path);
- a->logmsg_path = NULL;
- err = got_error_msg(GOT_ERR_COMMIT_MSG_EMPTY,
- "commit message cannot be empty, aborting");
- goto done;
- }
+ err = edit_logmsg(logmsg, a->editor, a->logmsg_path, initial_content);
done:
+ unlink(a->logmsg_path);
+ free(a->logmsg_path);
free(initial_content);
free(template);
/* Editor is done; we can now apply unveil(2) */
- if (err == NULL)
+ if (err == NULL) {
err = apply_unveil(a->repo_path, 0, a->worktree_path, 0);
+ if (err) {
+ free(*logmsg);
+ *logmsg = NULL;
+ }
+ }
return err;
}
blob - d4ac915a022f60064bf883eaa2bc5b4bb7355611
blob + 6b8435a8f69b24786fa17e77464465e2339529e4
--- include/got_repository.h
+++ include/got_repository.h
*/
struct got_repository;
+struct got_pathlist_head;
/* Open and close repositories. */
const struct got_error *got_repo_open(struct got_repository**, const char *);
/* Attempt to find a unique object ID for a given ID string prefix. */
const struct got_error *got_repo_match_object_id_prefix(struct got_object_id **,
const char *, int, struct got_repository *);
+
+/* A callback function which is invoked when a path is imported. */
+typedef const struct got_error *(*got_repo_import_cb)(void *, const char *);
+
+/*
+ * Import an unversioned directory tree into the repository.
+ * Creates a root commit, i.e. a commit with zero parents.
+ */
+const struct got_error *got_repo_import(struct got_object_id **, const char *,
+ const char *, const char *, struct got_pathlist_head *,
+ struct got_repository *, got_repo_import_cb, void *);
blob - feb2dcacbe2d9ef61576ea6b746e5588d5a1d92a
blob + 867b2d236255af662c58302c5e1d984d299dbc85
--- lib/object_create.c
+++ lib/object_create.c
goto done;
}
- SIMPLEQ_FOREACH(qid, parent_ids, entry) {
- char *parent_str = NULL;
+ if (parent_ids) {
+ SIMPLEQ_FOREACH(qid, parent_ids, entry) {
+ char *parent_str = NULL;
- free(id_str);
+ free(id_str);
- err = got_object_id_str(&id_str, qid->id);
- if (err)
- goto done;
- if (asprintf(&parent_str, "%s%s\n", GOT_COMMIT_LABEL_PARENT,
- id_str) == -1) {
- err = got_error_from_errno("asprintf");
- goto done;
- }
- len = strlen(parent_str);
- SHA1Update(&sha1_ctx, parent_str, len);
- n = fwrite(parent_str, 1, len, commitfile);
- if (n != len) {
- err = got_ferror(commitfile, GOT_ERR_IO);
+ err = got_object_id_str(&id_str, qid->id);
+ if (err)
+ goto done;
+ if (asprintf(&parent_str, "%s%s\n",
+ GOT_COMMIT_LABEL_PARENT, id_str) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+ len = strlen(parent_str);
+ SHA1Update(&sha1_ctx, parent_str, len);
+ n = fwrite(parent_str, 1, len, commitfile);
+ if (n != len) {
+ err = got_ferror(commitfile, GOT_ERR_IO);
+ free(parent_str);
+ goto done;
+ }
free(parent_str);
- goto done;
}
- free(parent_str);
}
len = strlen(author_str);
blob - c8076c6355d6683f3fadaa770be0345c15825f48
blob + 39c436e7f663ea304f8d339f4eebc4f646156e15
--- lib/repository.c
+++ lib/repository.c
#include <ctype.h>
#include <fcntl.h>
+#include <fnmatch.h>
#include <limits.h>
#include <dirent.h>
#include <stdlib.h>
#include "got_lib_delta.h"
#include "got_lib_inflate.h"
#include "got_lib_object.h"
+#include "got_lib_object_parse.h"
+#include "got_lib_object_create.h"
#include "got_lib_pack.h"
#include "got_lib_privsep.h"
#include "got_lib_worktree.h"
return err;
}
+
+static const struct got_error *
+alloc_added_blob_tree_entry(struct got_tree_entry **new_te,
+ const char *name, mode_t mode, struct got_object_id *blob_id)
+{
+ const struct got_error *err = NULL;
+
+ *new_te = NULL;
+
+ *new_te = calloc(1, sizeof(**new_te));
+ if (*new_te == NULL)
+ return got_error_from_errno("calloc");
+
+ (*new_te)->name = strdup(name);
+ if ((*new_te)->name == NULL) {
+ err = got_error_from_errno("strdup");
+ goto done;
+ }
+
+ (*new_te)->mode = S_IFREG | (mode & ((S_IRWXU | S_IRWXG | S_IRWXO)));
+ (*new_te)->id = blob_id;
+done:
+ if (err && *new_te) {
+ got_object_tree_entry_close(*new_te);
+ *new_te = NULL;
+ }
+ return err;
+}
+
+static const struct got_error *
+import_file(struct got_tree_entry **new_te, struct dirent *de,
+ const char *path, struct got_repository *repo)
+{
+ const struct got_error *err;
+ struct got_object_id *blob_id = NULL;
+ char *filepath;
+ struct stat sb;
+
+ if (asprintf(&filepath, "%s%s%s", path,
+ path[0] == '\0' ? "" : "/", de->d_name) == -1)
+ return got_error_from_errno("asprintf");
+
+ if (lstat(filepath, &sb) != 0) {
+ err = got_error_from_errno2("lstat", path);
+ goto done;
+ }
+
+ err = got_object_blob_create(&blob_id, filepath, repo);
+ if (err)
+ goto done;
+
+ err = alloc_added_blob_tree_entry(new_te, de->d_name, sb.st_mode,
+ blob_id);
+done:
+ free(filepath);
+ if (err)
+ free(blob_id);
+ return err;
+}
+
+static const struct got_error *
+insert_tree_entry(struct got_tree_entry *new_te,
+ struct got_pathlist_head *paths)
+{
+ const struct got_error *err = NULL;
+ struct got_pathlist_entry *new_pe;
+
+ err = got_pathlist_insert(&new_pe, paths, new_te->name, new_te);
+ if (err)
+ return err;
+ if (new_pe == NULL)
+ return got_error(GOT_ERR_TREE_DUP_ENTRY);
+ return NULL;
+}
+
+static const struct got_error *write_tree(struct got_object_id **,
+ const char *, struct got_pathlist_head *, struct got_repository *,
+ got_repo_import_cb progress_cb, void *progress_arg);
+
+static const struct got_error *
+import_subdir(struct got_tree_entry **new_te, struct dirent *de,
+ const char *path, struct got_pathlist_head *ignores,
+ struct got_repository *repo,
+ got_repo_import_cb progress_cb, void *progress_arg)
+{
+ const struct got_error *err;
+ char *subdirpath;
+
+ if (asprintf(&subdirpath, "%s%s%s", path,
+ path[0] == '\0' ? "" : "/", de->d_name) == -1)
+ return got_error_from_errno("asprintf");
+
+ (*new_te) = calloc(1, sizeof(**new_te));
+ (*new_te)->mode = S_IFDIR;
+ (*new_te)->name = strdup(de->d_name);
+ if ((*new_te)->name == NULL) {
+ err = got_error_from_errno("strdup");
+ goto done;
+ }
+
+ err = write_tree(&(*new_te)->id, subdirpath, ignores, repo,
+ progress_cb, progress_arg);
+done:
+ free(subdirpath);
+ if (err) {
+ got_object_tree_entry_close(*new_te);
+ *new_te = NULL;
+ }
+ return err;
+}
+
+static const struct got_error *
+write_tree(struct got_object_id **new_tree_id, const char *path_dir,
+ struct got_pathlist_head *ignores, struct got_repository *repo,
+ got_repo_import_cb progress_cb, void *progress_arg)
+{
+ const struct got_error *err = NULL;
+ DIR *dir;
+ struct dirent *de;
+ struct got_tree_entries new_tree_entries;
+ struct got_tree_entry *new_te = NULL;
+ struct got_pathlist_head paths;
+ struct got_pathlist_entry *pe;
+ char *name;
+
+ *new_tree_id = NULL;
+
+ TAILQ_INIT(&paths);
+ new_tree_entries.nentries = 0;
+ SIMPLEQ_INIT(&new_tree_entries.head);
+
+ dir = opendir(path_dir);
+ if (dir == NULL) {
+ err = got_error_from_errno2("opendir", path_dir);
+ goto done;
+ }
+
+ while ((de = readdir(dir)) != NULL) {
+ int ignore = 0;
+
+ if (strcmp(de->d_name, ".") == 0 ||
+ strcmp(de->d_name, "..") == 0)
+ continue;
+
+ TAILQ_FOREACH(pe, ignores, entry) {
+ if (fnmatch(pe->path, de->d_name, 0) == 0) {
+ ignore = 1;
+ break;
+ }
+ }
+ if (ignore)
+ continue;
+ if (de->d_type == DT_DIR) {
+ err = import_subdir(&new_te, de, path_dir,
+ ignores, repo, progress_cb, progress_arg);
+ if (err)
+ goto done;
+ } else if (de->d_type == DT_REG) {
+ err = import_file(&new_te, de, path_dir, repo);
+ if (err)
+ goto done;
+ } else
+ continue;
+
+ err = insert_tree_entry(new_te, &paths);
+ if (err)
+ goto done;
+ }
+
+ TAILQ_FOREACH(pe, &paths, entry) {
+ struct got_tree_entry *te = pe->data;
+ char *path;
+ new_tree_entries.nentries++;
+ SIMPLEQ_INSERT_TAIL(&new_tree_entries.head, te, entry);
+ if (!S_ISREG(te->mode))
+ continue;
+ if (asprintf(&path, "%s/%s", path_dir, pe->path) == -1) {
+ err = got_error_from_errno("asprintf");
+ goto done;
+ }
+ err = (*progress_cb)(progress_arg, path);
+ free(path);
+ if (err)
+ goto done;
+ }
+
+ name = basename(path_dir);
+ if (name == NULL) {
+ err = got_error_from_errno("basename");
+ goto done;
+ }
+
+ err = got_object_tree_create(new_tree_id, &new_tree_entries, repo);
+done:
+ if (dir)
+ closedir(dir);
+ got_object_tree_entries_close(&new_tree_entries);
+ got_pathlist_free(&paths);
+ return err;
+}
+
+const struct got_error *
+got_repo_import(struct got_object_id **new_commit_id, const char *path_dir,
+ const char *logmsg, const char *author, struct got_pathlist_head *ignores,
+ struct got_repository *repo, got_repo_import_cb progress_cb,
+ void *progress_arg)
+{
+ const struct got_error *err;
+ struct got_object_id *new_tree_id;
+
+ err = write_tree(&new_tree_id, path_dir, ignores, repo,
+ progress_cb, progress_arg);
+ if (err)
+ return err;
+
+ err = got_object_commit_create(new_commit_id, new_tree_id, NULL, 0,
+ author, time(NULL), author, time(NULL), logmsg, repo);
+ free(new_tree_id);
+ return err;
+}
blob - cc53c4052b7de555d29b4d516d502908cf15873f
blob + 682c9e1542649798fd47bc71d7532329397d40f9
--- regress/cmdline/Makefile
+++ regress/cmdline/Makefile
REGRESS_TARGETS=checkout update status log add rm diff commit \
- cherrypick backout rebase
+ cherrypick backout rebase import
NOOBJ=Yes
checkout:
rebase:
./rebase.sh
+import:
+ ./import.sh
+
.include <bsd.regress.mk>
blob - 793da6f9f59ddd31762b67fadea5b2769de756cf
blob + 4b6b06494055b3a907d857c1b28fb33f12c57ada
--- regress/cmdline/common.sh
+++ regress/cmdline/common.sh
echo delta > $repo/gamma/delta
mkdir $repo/epsilon
echo zeta > $repo/epsilon/zeta
- (cd $repo && git add .)
}
+function get_blob_id
+{
+ repo="$1"
+ tree_path="$2"
+ filename="$3"
+
+ got tree -r $repo -i $tree_path | grep ${filename}$ | cut -d' ' -f 1
+}
+
function test_init
{
local testname="$1"
git_init $testroot/repo
if [ -z "$no_tree" ]; then
make_test_tree $testroot/repo
+ (cd $repo && git add .)
git_commit $testroot/repo -m "adding the test tree"
fi
echo "$testroot"
blob - /dev/null
blob + bf8a0681d0998fa6be6c82f3b8fb3dde90f1cbba (mode 755)
--- /dev/null
+++ regress/cmdline/import.sh
+#!/bin/sh
+#
+# Copyright (c) 2019 Stefan Sperling <stsp@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+. ./common.sh
+
+function test_import_basic {
+ local testroot=`mktemp -p /tmp -d got-test-$testname-XXXXXXXX`
+
+ got init $testroot/repo
+
+ mkdir $testroot/tree
+ make_test_tree $testroot/tree
+
+ got import -m 'init' -r $testroot/repo $testroot/tree \
+ > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ local head_commit=`git_show_head $testroot/repo`
+ echo "A $testroot/tree/gamma/delta" > $testroot/stdout.expected
+ echo "A $testroot/tree/epsilon/zeta" >> $testroot/stdout.expected
+ echo "A $testroot/tree/alpha" >> $testroot/stdout.expected
+ echo "A $testroot/tree/beta" >> $testroot/stdout.expected
+ echo "Created branch refs/heads/master with commit $head_commit" \
+ >> $testroot/stdout.expected
+
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ (cd $testroot/repo && got log -p | grep -v ^date: > $testroot/stdout)
+
+ id_alpha=`get_blob_id $testroot/repo "" alpha`
+ id_beta=`get_blob_id $testroot/repo "" beta`
+ id_zeta=`get_blob_id $testroot/repo epsilon zeta`
+ id_delta=`get_blob_id $testroot/repo gamma delta`
+
+ echo "-----------------------------------------------" \
+ > $testroot/stdout.expected
+ echo "commit $head_commit (master)" >> $testroot/stdout.expected
+ echo "from: $GOT_AUTHOR" >> $testroot/stdout.expected
+ echo " " >> $testroot/stdout.expected
+ echo " init" >> $testroot/stdout.expected
+ echo " " >> $testroot/stdout.expected
+ echo "diff /dev/null $head_commit" >> $testroot/stdout.expected
+ echo "blob - /dev/null" >> $testroot/stdout.expected
+ echo "blob + $id_alpha" >> $testroot/stdout.expected
+ echo "--- /dev/null" >> $testroot/stdout.expected
+ echo "+++ alpha" >> $testroot/stdout.expected
+ echo "@@ -0,0 +1 @@" >> $testroot/stdout.expected
+ echo "+alpha" >> $testroot/stdout.expected
+ echo "blob - /dev/null" >> $testroot/stdout.expected
+ echo "blob + $id_beta" >> $testroot/stdout.expected
+ echo "--- /dev/null" >> $testroot/stdout.expected
+ echo "+++ beta" >> $testroot/stdout.expected
+ echo "@@ -0,0 +1 @@" >> $testroot/stdout.expected
+ echo "+beta" >> $testroot/stdout.expected
+ echo "blob - /dev/null" >> $testroot/stdout.expected
+ echo "blob + $id_zeta" >> $testroot/stdout.expected
+ echo "--- /dev/null" >> $testroot/stdout.expected
+ echo "+++ epsilon/zeta" >> $testroot/stdout.expected
+ echo "@@ -0,0 +1 @@" >> $testroot/stdout.expected
+ echo "+zeta" >> $testroot/stdout.expected
+ echo "blob - /dev/null" >> $testroot/stdout.expected
+ echo "blob + $id_delta" >> $testroot/stdout.expected
+ echo "--- /dev/null" >> $testroot/stdout.expected
+ echo "+++ gamma/delta" >> $testroot/stdout.expected
+ echo "@@ -0,0 +1 @@" >> $testroot/stdout.expected
+ echo "+delta" >> $testroot/stdout.expected
+ echo "" >> $testroot/stdout.expected
+
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo "A $testroot/wt/alpha" > $testroot/stdout.expected
+ echo "A $testroot/wt/beta" >> $testroot/stdout.expected
+ echo "A $testroot/wt/epsilon/zeta" >> $testroot/stdout.expected
+ echo "A $testroot/wt/gamma/delta" >> $testroot/stdout.expected
+ echo "Now shut up and hack" >> $testroot/stdout.expected
+
+ got checkout $testroot/repo $testroot/wt > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ echo "alpha" > $testroot/content.expected
+ echo "beta" >> $testroot/content.expected
+ echo "zeta" >> $testroot/content.expected
+ echo "delta" >> $testroot/content.expected
+ cat $testroot/wt/alpha $testroot/wt/beta $testroot/wt/epsilon/zeta \
+ $testroot/wt/gamma/delta > $testroot/content
+
+ cmp -s $testroot/content.expected $testroot/content
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/content.expected $testroot/content
+ fi
+ test_done "$testroot" "$ret"
+}
+
+function test_import_requires_new_branch {
+ local testroot=`test_init import_requires_new_branch`
+
+ mkdir $testroot/tree
+ make_test_tree $testroot/tree
+
+ got import -m 'init' -r $testroot/repo $testroot/tree \
+ > $testroot/stdout 2> $testroot/stderr
+ ret="$?"
+ if [ "$ret" == "0" ]; then
+ echo "import command should have failed but did not"
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ echo "got: import target branch already exists" \
+ > $testroot/stderr.expected
+ cmp -s $testroot/stderr.expected $testroot/stderr
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stderr.expected $testroot/stderr
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got import -b newbranch -m 'init' -r $testroot/repo $testroot/tree \
+ > $testroot/stdout
+ ret="$?"
+ test_done "$testroot" "$ret"
+
+}
+
+function test_import_ignores {
+ local testroot=`mktemp -p /tmp -d got-test-$testname-XXXXXXXX`
+
+ got init $testroot/repo
+
+ mkdir $testroot/tree
+ make_test_tree $testroot/tree
+
+ got import -I alpha -I '*lta*' -I '*silon' \
+ -m 'init' -r $testroot/repo $testroot/tree > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ local head_commit=`git_show_head $testroot/repo`
+ echo "A $testroot/tree/beta" >> $testroot/stdout.expected
+ echo "Created branch refs/heads/master with commit $head_commit" \
+ >> $testroot/stdout.expected
+
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ fi
+ test_done "$testroot" "$ret"
+
+
+}
+
+run_test test_import_basic
+run_test test_import_requires_new_branch
+run_test test_import_ignores