void home_config_paths(char **global, char **xdg, char *file) { char *xdg_home = getenv("XDG_CONFIG_HOME"); const char *home = get_home_directory(); char *to_free = NULL; if (!home) { if (global) *global = NULL; } else { if (!xdg_home) { to_free = mkpathdup("%s/.config", home); xdg_home = to_free; } if (global) *global = mkpathdup("%s/.gitconfig", home); } if (!xdg_home) *xdg = NULL; else *xdg = mkpathdup("%s/git/%s", xdg_home, file); free(to_free); }
char *xdg_config_home(const char *filename) { const char *home, *config_home; assert(filename); config_home = getenv("XDG_CONFIG_HOME"); if (config_home && *config_home) return mkpathdup("%s/git/%s", config_home, filename); home = getenv("HOME"); if (home) return mkpathdup("%s/.config/git/%s", home, filename); return NULL; }
char *xdg_cache_home(const char *filename) { const char *home, *cache_home; assert(filename); cache_home = getenv("XDG_CACHE_HOME"); if (cache_home && *cache_home) return mkpathdup("%s/git/%s", cache_home, filename); home = getenv("HOME"); if (home) return mkpathdup("%s/.cache/git/%s", home, filename); return NULL; }
static int add_one_reference(struct string_list_item *item, void *cb_data) { char *ref_git; const char *repo; struct strbuf alternate = STRBUF_INIT; /* Beware: read_gitfile(), real_path() and mkpath() return static buffer */ ref_git = xstrdup(real_path(item->string)); repo = read_gitfile(ref_git); if (!repo) repo = read_gitfile(mkpath("%s/.git", ref_git)); if (repo) { free(ref_git); ref_git = xstrdup(repo); } if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) { char *ref_git_git = mkpathdup("%s/.git", ref_git); free(ref_git); ref_git = ref_git_git; } else if (!is_directory(mkpath("%s/objects", ref_git))) die(_("reference repository '%s' is not a local repository."), item->string); strbuf_addf(&alternate, "%s/objects", ref_git); add_to_alternates_file(alternate.buf); strbuf_release(&alternate); free(ref_git); return 0; }
static void copy_alternates(struct strbuf *src, struct strbuf *dst, const char *src_repo) { /* * Read from the source objects/info/alternates file * and copy the entries to corresponding file in the * destination repository with add_to_alternates_file(). * Both src and dst have "$path/objects/info/alternates". * * Instead of copying bit-for-bit from the original, * we need to append to existing one so that the already * created entry via "clone -s" is not lost, and also * to turn entries with paths relative to the original * absolute, so that they can be used in the new repository. */ FILE *in = fopen(src->buf, "r"); struct strbuf line = STRBUF_INIT; while (strbuf_getline(&line, in, '\n') != EOF) { char *abs_path; if (!line.len || line.buf[0] == '#') continue; if (is_absolute_path(line.buf)) { add_to_alternates_file(line.buf); continue; } abs_path = mkpathdup("%s/objects/%s", src_repo, line.buf); normalize_path_copy(abs_path, abs_path); add_to_alternates_file(abs_path); free(abs_path); } strbuf_release(&line); fclose(in); }
int cmd_prune(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct progress *progress = NULL; const struct option options[] = { OPT__DRY_RUN(&show_only, N_("do not remove, show only")), OPT__VERBOSE(&verbose, N_("report pruned objects")), OPT_BOOL(0, "progress", &show_progress, N_("show progress")), OPT_EXPIRY_DATE(0, "expire", &expire, N_("expire objects older than <time>")), OPT_END() }; char *s; expire = TIME_MAX; save_commit_buffer = 0; check_replace_refs = 0; ref_paranoia = 1; init_revisions(&revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); if (repository_format_precious_objects) die(_("cannot prune in a precious-objects repo")); while (argc--) { struct object_id oid; const char *name = *argv++; if (!get_oid(name, &oid)) { struct object *object = parse_object_or_die(&oid, name); add_pending_object(&revs, object, ""); } else die("unrecognized argument: %s", name); } if (show_progress == -1) show_progress = isatty(2); if (show_progress) progress = start_progress_delay(_("Checking connectivity"), 0, 0, 2); mark_reachable_objects(&revs, 1, expire, progress); stop_progress(&progress); for_each_loose_file_in_objdir(get_object_directory(), prune_object, prune_cruft, prune_subdir, NULL); prune_packed_objects(show_only ? PRUNE_PACKED_DRY_RUN : 0); remove_temporary_files(get_object_directory()); s = mkpathdup("%s/pack", get_object_directory()); remove_temporary_files(s); free(s); if (is_repository_shallow()) prune_shallow(show_only); return 0; }
static int update_info_packs(int force) { char *infofile = mkpathdup("%s/info/packs", get_object_directory()); int ret; init_pack_info(infofile, force); ret = update_info_file(infofile, write_pack_info_file); free_pack_info(); free(infofile); return ret; }
int cmd_prune(int argc, const char **argv, const char *prefix) { struct rev_info revs; struct progress *progress = NULL; const struct option options[] = { OPT__DRY_RUN(&show_only, N_("do not remove, show only")), OPT__VERBOSE(&verbose, N_("report pruned objects")), OPT_BOOL(0, "progress", &show_progress, N_("show progress")), OPT_DATE(0, "expire", &expire, N_("expire objects older than <time>")), OPT_END() }; char *s; expire = ULONG_MAX; save_commit_buffer = 0; read_replace_refs = 0; init_revisions(&revs, prefix); argc = parse_options(argc, argv, prefix, options, prune_usage, 0); while (argc--) { unsigned char sha1[20]; const char *name = *argv++; if (!get_sha1(name, sha1)) { struct object *object = parse_object(sha1); if (!object) die("bad object: %s", name); add_pending_object(&revs, object, ""); } else die("unrecognized argument: %s", name); } if (show_progress == -1) show_progress = isatty(2); if (show_progress) progress = start_progress_delay("Checking connectivity", 0, 0, 2); mark_reachable_objects(&revs, 1, progress); stop_progress(&progress); prune_object_dir(get_object_directory()); prune_packed_objects(show_only); remove_temporary_files(get_object_directory()); s = mkpathdup("%s/pack", get_object_directory()); remove_temporary_files(s); free(s); return 0; }
static int add_one_reference(struct string_list_item *item, void *cb_data) { char *ref_git; const char *repo; struct strbuf alternate = STRBUF_INIT; /* Beware: read_gitfile(), real_path() and mkpath() return static buffer */ ref_git = xstrdup(real_path(item->string)); repo = read_gitfile(ref_git); if (!repo) repo = read_gitfile(mkpath("%s/.git", ref_git)); if (repo) { free(ref_git); ref_git = xstrdup(repo); } if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) { char *ref_git_git = mkpathdup("%s/.git", ref_git); free(ref_git); ref_git = ref_git_git; } else if (!is_directory(mkpath("%s/objects", ref_git))) { struct strbuf sb = STRBUF_INIT; if (get_common_dir(&sb, ref_git)) die(_("reference repository '%s' as a linked checkout is not supported yet."), item->string); die(_("reference repository '%s' is not a local repository."), item->string); } if (!access(mkpath("%s/shallow", ref_git), F_OK)) die(_("reference repository '%s' is shallow"), item->string); if (!access(mkpath("%s/info/grafts", ref_git), F_OK)) die(_("reference repository '%s' is grafted"), item->string); strbuf_addf(&alternate, "%s/objects", ref_git); add_to_alternates_file(alternate.buf); strbuf_release(&alternate); free(ref_git); return 0; }
/* * Create the file "path" by writing to a temporary file and renaming * it into place. The contents of the file come from "generate", which * should return non-zero if it encounters an error. */ static int update_info_file(char *path, int (*generate)(FILE *)) { char *tmp = mkpathdup("%s_XXXXXX", path); int ret = -1; int fd = -1; FILE *fp = NULL; safe_create_leading_directories(path); fd = git_mkstemp_mode(tmp, 0666); if (fd < 0) goto out; fp = fdopen(fd, "w"); if (!fp) goto out; ret = generate(fp); if (ret) goto out; if (fclose(fp)) goto out; if (adjust_shared_perm(tmp) < 0) goto out; if (rename(tmp, path) < 0) goto out; ret = 0; out: if (ret) { error_errno("unable to update %s", path); if (fp) fclose(fp); else if (fd >= 0) close(fd); unlink(tmp); } free(tmp); return ret; }
int cmd_clone(int argc, const char **argv, const char *prefix) { int is_bundle = 0, is_local; struct stat buf; const char *repo_name, *repo, *work_tree, *git_dir; char *path, *dir; int dest_exists; const struct ref *refs, *remote_head; const struct ref *remote_head_points_at; const struct ref *our_head_points_at; struct ref *mapped_refs; const struct ref *ref; struct strbuf key = STRBUF_INIT, value = STRBUF_INIT; struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT; struct transport *transport = NULL; const char *src_ref_prefix = "refs/heads/"; struct remote *remote; int err = 0, complete_refs_before_fetch = 1; struct refspec *refspec; const char *fetch_pattern; packet_trace_identity("clone"); argc = parse_options(argc, argv, prefix, builtin_clone_options, builtin_clone_usage, 0); if (argc > 2) usage_msg_opt(_("Too many arguments."), builtin_clone_usage, builtin_clone_options); if (argc == 0) usage_msg_opt(_("You must specify a repository to clone."), builtin_clone_usage, builtin_clone_options); if (option_single_branch == -1) option_single_branch = option_depth ? 1 : 0; if (option_mirror) option_bare = 1; if (option_bare) { if (option_origin) die(_("--bare and --origin %s options are incompatible."), option_origin); if (real_git_dir) die(_("--bare and --separate-git-dir are incompatible.")); option_no_checkout = 1; } if (!option_origin) option_origin = "origin"; repo_name = argv[0]; path = get_repo_path(repo_name, &is_bundle); if (path) repo = xstrdup(absolute_path(repo_name)); else if (!strchr(repo_name, ':')) die(_("repository '%s' does not exist"), repo_name); else repo = repo_name; /* no need to be strict, transport_set_option() will validate it again */ if (option_depth && atoi(option_depth) < 1) die(_("depth %s is not a positive number"), option_depth); if (argc == 2) dir = xstrdup(argv[1]); else dir = guess_dir_name(repo_name, is_bundle, option_bare); strip_trailing_slashes(dir); dest_exists = !stat(dir, &buf); if (dest_exists && !is_empty_dir(dir)) die(_("destination path '%s' already exists and is not " "an empty directory."), dir); strbuf_addf(&reflog_msg, "clone: from %s", repo); if (option_bare) work_tree = NULL; else { work_tree = getenv("GIT_WORK_TREE"); if (work_tree && !stat(work_tree, &buf)) die(_("working tree '%s' already exists."), work_tree); } if (option_bare || work_tree) git_dir = xstrdup(dir); else { work_tree = dir; git_dir = mkpathdup("%s/.git", dir); } if (!option_bare) { junk_work_tree = work_tree; if (safe_create_leading_directories_const(work_tree) < 0) die_errno(_("could not create leading directories of '%s'"), work_tree); if (!dest_exists && mkdir(work_tree, 0777)) die_errno(_("could not create work tree dir '%s'."), work_tree); set_git_work_tree(work_tree); } junk_git_dir = git_dir; atexit(remove_junk); sigchain_push_common(remove_junk_on_signal); if (safe_create_leading_directories_const(git_dir) < 0) die(_("could not create leading directories of '%s'"), git_dir); set_git_dir_init(git_dir, real_git_dir, 0); if (real_git_dir) { git_dir = real_git_dir; junk_git_dir = real_git_dir; } if (0 <= option_verbosity) { if (option_bare) fprintf(stderr, _("Cloning into bare repository '%s'...\n"), dir); else fprintf(stderr, _("Cloning into '%s'...\n"), dir); } init_db(option_template, INIT_DB_QUIET); write_config(&option_config); git_config(git_default_config, NULL); if (option_bare) { if (option_mirror) src_ref_prefix = "refs/"; strbuf_addstr(&branch_top, src_ref_prefix); git_config_set("core.bare", "true"); } else { strbuf_addf(&branch_top, "refs/remotes/%s/", option_origin); } strbuf_addf(&value, "+%s*:%s*", src_ref_prefix, branch_top.buf); strbuf_addf(&key, "remote.%s.url", option_origin); git_config_set(key.buf, repo); strbuf_reset(&key); if (option_reference.nr) setup_reference(); else if (option_dissociate) { warning(_("--dissociate given, but there is no --reference")); option_dissociate = 0; } fetch_pattern = value.buf; refspec = parse_fetch_refspec(1, &fetch_pattern); strbuf_reset(&value); remote = remote_get(option_origin); transport = transport_get(remote, remote->url[0]); path = get_repo_path(remote->url[0], &is_bundle); is_local = option_local != 0 && path && !is_bundle; if (is_local) { if (option_depth) warning(_("--depth is ignored in local clones; use file:// instead.")); if (!access(mkpath("%s/shallow", path), F_OK)) { if (option_local > 0) warning(_("source repository is shallow, ignoring --local")); is_local = 0; } } if (option_local > 0 && !is_local) warning(_("--local is ignored")); transport->cloning = 1; if (!transport->get_refs_list || (!is_local && !transport->fetch)) die(_("Don't know how to clone %s"), transport->url); transport_set_option(transport, TRANS_OPT_KEEP, "yes"); if (option_depth) transport_set_option(transport, TRANS_OPT_DEPTH, option_depth); if (option_single_branch) transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1"); transport_set_verbosity(transport, option_verbosity, option_progress); if (option_upload_pack) transport_set_option(transport, TRANS_OPT_UPLOADPACK, option_upload_pack); if (transport->smart_options && !option_depth) transport->smart_options->check_self_contained_and_connected = 1; refs = transport_get_remote_refs(transport); if (refs) { mapped_refs = wanted_peer_refs(refs, refspec); /* * transport_get_remote_refs() may return refs with null sha-1 * in mapped_refs (see struct transport->get_refs_list * comment). In that case we need fetch it early because * remote_head code below relies on it. * * for normal clones, transport_get_remote_refs() should * return reliable ref set, we can delay cloning until after * remote HEAD check. */ for (ref = refs; ref; ref = ref->next) if (is_null_sha1(ref->old_sha1)) { complete_refs_before_fetch = 0; break; } if (!is_local && !complete_refs_before_fetch) transport_fetch_refs(transport, mapped_refs); remote_head = find_ref_by_name(refs, "HEAD"); remote_head_points_at = guess_remote_head(remote_head, mapped_refs, 0); if (option_branch) { our_head_points_at = find_remote_branch(mapped_refs, option_branch); if (!our_head_points_at) die(_("Remote branch %s not found in upstream %s"), option_branch, option_origin); } else our_head_points_at = remote_head_points_at; } else { if (option_branch) die(_("Remote branch %s not found in upstream %s"), option_branch, option_origin); warning(_("You appear to have cloned an empty repository.")); mapped_refs = NULL; our_head_points_at = NULL; remote_head_points_at = NULL; remote_head = NULL; option_no_checkout = 1; if (!option_bare) install_branch_config(0, "master", option_origin, "refs/heads/master"); } write_refspec_config(src_ref_prefix, our_head_points_at, remote_head_points_at, &branch_top); if (is_local) clone_local(path, git_dir); else if (refs && complete_refs_before_fetch) transport_fetch_refs(transport, mapped_refs); update_remote_refs(refs, mapped_refs, remote_head_points_at, branch_top.buf, reflog_msg.buf, transport, !is_local); update_head(our_head_points_at, remote_head, reflog_msg.buf); transport_unlock_pack(transport); transport_disconnect(transport); if (option_dissociate) dissociate_from_references(); junk_mode = JUNK_LEAVE_REPO; err = checkout(); strbuf_release(&reflog_msg); strbuf_release(&branch_top); strbuf_release(&key); strbuf_release(&value); junk_mode = JUNK_LEAVE_ALL; free(refspec); return err; }
static void update_refs_for_switch(const struct checkout_opts *opts, struct branch_info *old_branch_info, struct branch_info *new_branch_info) { struct strbuf msg = STRBUF_INIT; const char *old_desc, *reflog_msg; if (opts->new_branch) { if (opts->new_orphan_branch) { char *refname; refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch); if (opts->new_branch_log && !should_autocreate_reflog(refname)) { int ret; struct strbuf err = STRBUF_INIT; ret = safe_create_reflog(refname, 1, &err); if (ret) { fprintf(stderr, _("Can not do reflog for '%s': %s\n"), opts->new_orphan_branch, err.buf); strbuf_release(&err); free(refname); return; } strbuf_release(&err); } free(refname); } else create_branch(the_repository, opts->new_branch, new_branch_info->name, opts->new_branch_force ? 1 : 0, opts->new_branch_force ? 1 : 0, opts->new_branch_log, opts->quiet, opts->track); new_branch_info->name = opts->new_branch; setup_branch_path(new_branch_info); } old_desc = old_branch_info->name; if (!old_desc && old_branch_info->commit) old_desc = oid_to_hex(&old_branch_info->commit->object.oid); reflog_msg = getenv("GIT_REFLOG_ACTION"); if (!reflog_msg) strbuf_addf(&msg, "checkout: moving from %s to %s", old_desc ? old_desc : "(invalid)", new_branch_info->name); else strbuf_insert(&msg, 0, reflog_msg, strlen(reflog_msg)); if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) { /* Nothing to do. */ } else if (opts->force_detach || !new_branch_info->path) { /* No longer on any branch. */ update_ref(msg.buf, "HEAD", &new_branch_info->commit->object.oid, NULL, REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR); if (!opts->quiet) { if (old_branch_info->path && advice_detached_head && !opts->force_detach) detach_advice(new_branch_info->name); describe_detached_head(_("HEAD is now at"), new_branch_info->commit); } } else if (new_branch_info->path) { /* Switch branches. */ if (create_symref("HEAD", new_branch_info->path, msg.buf) < 0) die(_("unable to update HEAD")); if (!opts->quiet) { if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) { if (opts->new_branch_force) fprintf(stderr, _("Reset branch '%s'\n"), new_branch_info->name); else fprintf(stderr, _("Already on '%s'\n"), new_branch_info->name); } else if (opts->new_branch) { if (opts->branch_exists) fprintf(stderr, _("Switched to and reset branch '%s'\n"), new_branch_info->name); else fprintf(stderr, _("Switched to a new branch '%s'\n"), new_branch_info->name); } else { fprintf(stderr, _("Switched to branch '%s'\n"), new_branch_info->name); } } if (old_branch_info->path && old_branch_info->name) { if (!ref_exists(old_branch_info->path) && reflog_exists(old_branch_info->path)) delete_reflog(old_branch_info->path); } } remove_branch_state(the_repository); strbuf_release(&msg); if (!opts->quiet && (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD")))) report_tracking(new_branch_info); }
static int delete_branches(int argc, const char **argv, int force, int kinds, int quiet) { struct commit *head_rev = NULL; unsigned char sha1[20]; char *name = NULL; const char *fmt; int i; int ret = 0; int remote_branch = 0; struct strbuf bname = STRBUF_INIT; switch (kinds) { case REF_REMOTE_BRANCH: fmt = "refs/remotes/%s"; /* For subsequent UI messages */ remote_branch = 1; force = 1; break; case REF_LOCAL_BRANCH: fmt = "refs/heads/%s"; break; default: die(_("cannot use -a with -d")); } if (!force) { head_rev = lookup_commit_reference(head_sha1); if (!head_rev) die(_("Couldn't look up commit object for HEAD")); } for (i = 0; i < argc; i++, strbuf_release(&bname)) { const char *target; int flags = 0; strbuf_branchname(&bname, argv[i]); if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) { error(_("Cannot delete the branch '%s' " "which you are currently on."), bname.buf); ret = 1; continue; } free(name); name = mkpathdup(fmt, bname.buf); target = resolve_ref_unsafe(name, sha1, 0, &flags); if (!target || (!(flags & REF_ISSYMREF) && is_null_sha1(sha1))) { error(remote_branch ? _("remote branch '%s' not found.") : _("branch '%s' not found."), bname.buf); ret = 1; continue; } if (!(flags & REF_ISSYMREF) && check_branch_commit(bname.buf, name, sha1, head_rev, kinds, force)) { ret = 1; continue; } if (delete_ref(name, sha1, REF_NODEREF)) { error(remote_branch ? _("Error deleting remote branch '%s'") : _("Error deleting branch '%s'"), bname.buf); ret = 1; continue; } if (!quiet) { printf(remote_branch ? _("Deleted remote branch %s (was %s).\n") : _("Deleted branch %s (was %s).\n"), bname.buf, (flags & REF_ISSYMREF) ? target : find_unique_abbrev(sha1, DEFAULT_ABBREV)); } delete_branch_config(bname.buf); } free(name); return(ret); }
static int delete_branches(int argc, const char **argv, int force, int kinds, int quiet) { struct commit *head_rev = NULL; unsigned char sha1[20]; char *name = NULL; const char *fmt; int i; int ret = 0; int remote_branch = 0; struct strbuf bname = STRBUF_INIT; switch (kinds) { case FILTER_REFS_REMOTES: fmt = "refs/remotes/%s"; /* For subsequent UI messages */ remote_branch = 1; force = 1; break; case FILTER_REFS_BRANCHES: fmt = "refs/heads/%s"; break; default: die(_("cannot use -a with -d")); } if (!force) { head_rev = lookup_commit_reference(head_sha1); if (!head_rev) die(_("Couldn't look up commit object for HEAD")); } for (i = 0; i < argc; i++, strbuf_release(&bname)) { char *target = NULL; int flags = 0; strbuf_branchname(&bname, argv[i]); free(name); name = mkpathdup(fmt, bname.buf); if (kinds == FILTER_REFS_BRANCHES) { const struct worktree *wt = find_shared_symref("HEAD", name); if (wt) { error(_("Cannot delete branch '%s' " "checked out at '%s'"), bname.buf, wt->path); ret = 1; continue; } } target = resolve_refdup(name, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE | RESOLVE_REF_ALLOW_BAD_NAME, sha1, &flags); if (!target) { error(remote_branch ? _("remote-tracking branch '%s' not found.") : _("branch '%s' not found."), bname.buf); ret = 1; continue; } if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) && check_branch_commit(bname.buf, name, sha1, head_rev, kinds, force)) { ret = 1; goto next; } if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1, REF_NODEREF)) { error(remote_branch ? _("Error deleting remote-tracking branch '%s'") : _("Error deleting branch '%s'"), bname.buf); ret = 1; goto next; } if (!quiet) { printf(remote_branch ? _("Deleted remote-tracking branch %s (was %s).\n") : _("Deleted branch %s (was %s).\n"), bname.buf, (flags & REF_ISBROKEN) ? "broken" : (flags & REF_ISSYMREF) ? target : find_unique_abbrev(sha1, DEFAULT_ABBREV)); } delete_branch_config(bname.buf); next: free(target); } free(name); return(ret); }