group-writable). When 'all' (or 'world' or 'everybody'), the
repository will be readable by all users, additionally to being
group-shareable. When 'umask' (or 'false'), git will use permissions
- reported by umask(2). See gitlink:git-init-db[1]. False by default.
+ reported by umask(2). See gitlink:git-init[1]. False by default.
core.warnAmbiguousRefs::
If true, git will warn you if the ref name you passed it is ambiguous
For our first example, we're going to start a totally new repository from
scratch, with no pre-existing files, and we'll call it `git-tutorial`.
To start up, create a subdirectory for it, change into that
-subdirectory, and initialize the git infrastructure with `git-init-db`:
+subdirectory, and initialize the git infrastructure with `git-init`:
------------------------------------------------
$ mkdir git-tutorial
$ cd git-tutorial
-$ git-init-db
+$ git-init
------------------------------------------------
to which git will reply
------------
Then, make that directory into a git repository by running
-`git init-db`, but this time, since its name is not the usual
+`git init`, but this time, since its name is not the usual
`.git`, we do things slightly differently:
------------
-$ GIT_DIR=my-git.git git-init-db
+$ GIT_DIR=my-git.git git-init
------------
Make sure this directory is available for others you want your
+
If other people are pulling from your repository over dumb
transport protocols (HTTP), you need to keep this repository
-'dumb transport friendly'. After `git init-db`,
+'dumb transport friendly'. After `git init`,
`$GIT_DIR/hooks/post-update` copied from the standard templates
would contain a call to `git-update-server-info` but the
`post-update` hook itself is disabled by default -- enable it
------------------------------------------------
$ mkdir /pub/my-repo.git
$ cd /pub/my-repo.git
-$ git --bare init-db --shared
+$ git --bare init --shared
$ git --bare fetch /home/alice/myproject master:master
------------------------------------------------
4. by using the -a switch with the 'commit' command to automatically "add"
changes from all known files i.e. files that have already been committed
- before, and perform the actual commit.
+ before, and to automatically "rm" files that have been
+ removed from the working tree, and perform the actual commit.
The gitlink:git-status[1] command can be used to obtain a
summary of what is included by any of the above for the next
Add Signed-off-by line at the end of the commit message.
--no-verify::
- By default, the command looks for suspicious lines the
- commit introduces, and aborts committing if there is one.
- The definition of 'suspicious lines' is currently the
- lines that has trailing whitespaces, and the lines whose
- indentation has a SP character immediately followed by a
- TAB character. This option turns off the check.
+ This option bypasses the pre-commit hook.
+ See also link:hooks.html[hooks].
-e|--edit::
The message taken from file with `-F`, command line with
'git-init-db' [--template=<template_directory>] [--shared[=<permissions>]]
-OPTIONS
--------
-
---
-
---template=<template_directory>::
-
-Provide the directory from which templates will be used. The default template
-directory is `/usr/share/git-core/templates`.
-
-When specified, `<template_directory>` is used as the source of the template
-files rather than the default. The template files include some directory
-structure, some suggested "exclude patterns", and copies of non-executing
-"hook" files. The suggested patterns and hook files are all modifiable and
-extensible.
-
---shared[={false|true|umask|group|all|world|everybody}]::
-
-Specify that the git repository is to be shared amongst several users. This
-allows users belonging to the same group to push into that
-repository. When specified, the config variable "core.sharedRepository" is
-set so that files and directories under `$GIT_DIR` are created with the
-requested permissions. When not specified, git will use permissions reported
-by umask(2).
-
-The option can have the following values, defaulting to 'group' if no value
-is given:
-
- - 'umask' (or 'false'): Use permissions reported by umask(2). The default,
- when `--shared` is not specified.
-
- - 'group' (or 'true'): Make the repository group-writable, (and g+sx, since
- the git group may be not the primary group of all users).
-
- - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
- readable by all users.
-
-By default, the configuration flag receive.denyNonFastforward is enabled
-in shared repositories, so that you cannot force a non fast-forwarding push
-into it.
-
---
-
-
DESCRIPTION
-----------
-This command creates an empty git repository - basically a `.git` directory
-with subdirectories for `objects`, `refs/heads`, `refs/tags`, and
-template files.
-An initial `HEAD` file that references the HEAD of the master branch
-is also created.
-
-If the `$GIT_DIR` environment variable is set then it specifies a path
-to use instead of `./.git` for the base of the repository.
-
-If the object storage directory is specified via the `$GIT_OBJECT_DIRECTORY`
-environment variable then the sha1 directories are created underneath -
-otherwise the default `$GIT_DIR/objects` directory is used.
-
-Running `git-init-db` in an existing repository is safe. It will not overwrite
-things that are already there. The primary reason for rerunning `git-init-db`
-is to pick up newly added templates.
-
-Note that `git-init` is the same as `git-init-db`.
-
-
-EXAMPLES
---------
-
-Start a new git repository for an existing code base::
-+
-----------------
-$ cd /path/to/my/codebase
-$ git-init-db <1>
-$ git-add . <2>
-----------------
-+
-<1> prepare /path/to/my/codebase/.git directory
-<2> add all existing file to the index
-
-
-Author
-------
-Written by Linus Torvalds <torvalds@osdl.org>
-
-Documentation
---------------
-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
-GIT
----
-Part of the gitlink:git[7] suite
+This is a synonym for gitlink:git-init[1]. Please refer to the
+documentation of that command.
-include::git-init-db.txt[]
+git-init(1)
+===========
+
+NAME
+----
+git-init - Creates an empty git repository
+
+
+SYNOPSIS
+--------
+'git-init' [--template=<template_directory>] [--shared[=<permissions>]]
+
+
+OPTIONS
+-------
+
+--
+
+--template=<template_directory>::
+
+Provide the directory from which templates will be used. The default template
+directory is `/usr/share/git-core/templates`.
+
+When specified, `<template_directory>` is used as the source of the template
+files rather than the default. The template files include some directory
+structure, some suggested "exclude patterns", and copies of non-executing
+"hook" files. The suggested patterns and hook files are all modifiable and
+extensible.
+
+--shared[={false|true|umask|group|all|world|everybody}]::
+
+Specify that the git repository is to be shared amongst several users. This
+allows users belonging to the same group to push into that
+repository. When specified, the config variable "core.sharedRepository" is
+set so that files and directories under `$GIT_DIR` are created with the
+requested permissions. When not specified, git will use permissions reported
+by umask(2).
+
+The option can have the following values, defaulting to 'group' if no value
+is given:
+
+ - 'umask' (or 'false'): Use permissions reported by umask(2). The default,
+ when `--shared` is not specified.
+
+ - 'group' (or 'true'): Make the repository group-writable, (and g+sx, since
+ the git group may be not the primary group of all users).
+
+ - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
+ readable by all users.
+
+By default, the configuration flag receive.denyNonFastforward is enabled
+in shared repositories, so that you cannot force a non fast-forwarding push
+into it.
+
+--
+
+
+DESCRIPTION
+-----------
+This command creates an empty git repository - basically a `.git` directory
+with subdirectories for `objects`, `refs/heads`, `refs/tags`, and
+template files.
+An initial `HEAD` file that references the HEAD of the master branch
+is also created.
+
+If the `$GIT_DIR` environment variable is set then it specifies a path
+to use instead of `./.git` for the base of the repository.
+
+If the object storage directory is specified via the `$GIT_OBJECT_DIRECTORY`
+environment variable then the sha1 directories are created underneath -
+otherwise the default `$GIT_DIR/objects` directory is used.
+
+Running `git-init` in an existing repository is safe. It will not overwrite
+things that are already there. The primary reason for rerunning `git-init`
+is to pick up newly added templates.
+
+Note that `git-init` is the same as `git-init-db`. The command
+was primarily meant to initialize the object database, but over
+time it has become responsible for setting up the other aspects
+of the repository, such as installing the default hooks and
+setting the configuration variables. The old name is retained
+for backward compatibility reasons.
+
+
+EXAMPLES
+--------
+
+Start a new git repository for an existing code base::
++
+----------------
+$ cd /path/to/my/codebase
+$ git-init <1>
+$ git-add . <2>
+----------------
++
+<1> prepare /path/to/my/codebase/.git directory
+<2> add all existing file to the index
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
------------
$ mkdir -p /home/sean/import/jam
$ cd /home/sean/import/jam
-$ git init-db
+$ git init
$ git p4import //public/jam jammy
------------
SYNOPSIS
--------
-'git-prune-packed' [-n]
+'git-prune-packed' [-n] [-q]
DESCRIPTION
Don't actually remove any objects, only show those that would have been
removed.
+-q::
+ Squelch the progress indicator.
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
--shared::
--template=<template_directory>::
Only used with the 'init' command.
- These are passed directly to gitlink:git-init-db[1].
+ These are passed directly to gitlink:git-init[1].
-r <ARG>::
--revision <ARG>::
Tracking and contributing to a the trunk of a Subversion-managed project:
------------------------------------------------------------------------
-# Initialize a repo (like git init-db):
+# Initialize a repo (like git init):
git-svn init http://svn.foo.org/project/trunk
# Fetch remote revisions:
git-svn fetch
'<<tracking-multiple-repos,Tracking Multiple Repositories or Branches>>'
------------------------------------------------------------------------
-# Initialize a repo (like git init-db):
+# Initialize a repo (like git init):
git-svn multi-init http://svn.foo.org/project \
-T trunk -b branches -t tags
# Fetch remote revisions:
Build pack idx file for an existing packed archive.
gitlink:git-init[1]::
-gitlink:git-init-db[1]::
- Creates an empty git object database, or reinitialize an
+ Creates an empty git repository, or reinitialize an
existing one.
gitlink:git-merge-file[1]::
means "grab the master branch head from the $URL and store
it as my origin branch head".
And `git push $URL refs/heads/master:refs/heads/to-upstream`
- means "publish my master branch head as to-upstream master head
+ means "publish my master branch head as to-upstream branch
at $URL". See also gitlink:git-push[1]
repository::
Hooks are little scripts you can place in `$GIT_DIR/hooks`
directory to trigger action at certain points. When
-`git-init-db` is run, a handful example hooks are copied in the
+`git-init` is run, a handful example hooks are copied in the
`hooks` directory of the new repository, but by default they are
all disabled. To enable a hook, make it executable with `chmod +x`.
Initialize a bare repository
$ cd my-new-repo.git
- $ git --bare init-db
+ $ git --bare init
Change the ownership to your web-server's credentials. Use "grep ^User
hooks::
Hooks are customization scripts used by various git
commands. A handful of sample hooks are installed when
- `git init-db` is run, but all of them are disabled by
+ `git init` is run, but all of them are disabled by
default. To enable, they need to be made executable.
Read link:hooks.html[hooks] for more details about
each hook.
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.4.5-rc0.GIT
+DEF_VER=v1.5.0-rc1.GIT
LF='
'
repository itself. For example, you could:
$ mkdir manual && cd manual
- $ git init-db
+ $ git init
$ git fetch-pack git://git.kernel.org/pub/scm/git/git.git man html |
while read a b
do
}
}
-static void print_ref_list(int kinds, int verbose, int abbrev)
+static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
{
int i;
struct ref_list ref_list;
qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
+ detached = (detached && (kinds & REF_LOCAL_BRANCH));
+ if (detached) {
+ struct ref_item item;
+ item.name = "(no branch)";
+ item.kind = REF_LOCAL_BRANCH;
+ hashcpy(item.sha1, head_sha1);
+ if (strlen(item.name) > ref_list.maxwidth)
+ ref_list.maxwidth = strlen(item.name);
+ print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1);
+ }
+
for (i = 0; i < ref_list.index; i++) {
- int current = (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
+ int current = !detached &&
+ (ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
!strcmp(ref_list.list[i].name, head);
print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
abbrev, current);
free_ref_list(&ref_list);
}
-static void create_branch(const char *name, const char *start,
+static void create_branch(const char *name, const char *start_name,
+ unsigned char *start_sha1,
int force, int reflog)
{
struct ref_lock *lock;
die("Cannot force update the current branch.");
}
- if (get_sha1(start, sha1) ||
- (commit = lookup_commit_reference(sha1)) == NULL)
- die("Not a valid branch point: '%s'.", start);
+ if (start_sha1)
+ /* detached HEAD */
+ hashcpy(sha1, start_sha1);
+ else if (get_sha1(start_name, sha1))
+ die("Not a valid object name: '%s'.", start_name);
+
+ if ((commit = lookup_commit_reference(sha1)) == NULL)
+ die("Not a valid branch point: '%s'.", start_name);
hashcpy(sha1, commit->object.sha1);
lock = lock_any_ref_for_update(ref, NULL);
if (reflog) {
log_all_ref_updates = 1;
- snprintf(msg, sizeof msg, "branch: Created from %s", start);
+ snprintf(msg, sizeof msg, "branch: Created from %s",
+ start_name);
}
if (write_ref_sha1(lock, sha1, msg) < 0)
char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
unsigned char sha1[20];
+ if (!oldname)
+ die("cannot rename the curren branch while not on any.");
+
if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref))
die("Old branchname too long");
{
int delete = 0, force_delete = 0, force_create = 0;
int rename = 0, force_rename = 0;
- int verbose = 0, abbrev = DEFAULT_ABBREV;
+ int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
int reflog = 0;
int kinds = REF_LOCAL_BRANCH;
int i;
head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL));
if (!head)
die("Failed to resolve HEAD as a valid ref.");
- if (strncmp(head, "refs/heads/", 11))
- die("HEAD not found below refs/heads!");
- head += 11;
+ if (!strcmp(head, "HEAD")) {
+ detached = 1;
+ }
+ else {
+ if (strncmp(head, "refs/heads/", 11))
+ die("HEAD not found below refs/heads!");
+ head += 11;
+ }
if (delete)
return delete_branches(argc - i, argv + i, force_delete, kinds);
else if (i == argc)
- print_ref_list(kinds, verbose, abbrev);
+ print_ref_list(kinds, detached, verbose, abbrev);
else if (rename && (i == argc - 1))
rename_branch(head, argv[i], force_rename);
else if (rename && (i == argc - 2))
rename_branch(argv[i], argv[i + 1], force_rename);
else if (i == argc - 1)
- create_branch(argv[i], head, force_create, reflog);
+ create_branch(argv[i], head, head_sha1, force_create, reflog);
else if (i == argc - 2)
- create_branch(argv[i], argv[i + 1], force_create, reflog);
+ create_branch(argv[i], argv[i+1], NULL, force_create, reflog);
else
usage(builtin_branch_usage);
#include "commit.h"
#include "tag.h"
#include "refs.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
#include "builtin.h"
-#define SEEN (1u << 0)
-
static const char describe_usage[] =
"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
static int names, allocs;
static struct commit_name {
- const struct commit *commit;
+ struct commit *commit;
int prio; /* annotated tag = 2, tag = 1, head = 0 */
char path[FLEX_ARRAY]; /* more */
} **name_array = NULL;
}
static void add_to_known_names(const char *path,
- const struct commit *commit,
+ struct commit *commit,
int prio)
{
int idx;
return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
}
+struct possible_tag {
+ struct possible_tag *next;
+ struct commit_name *name;
+ unsigned long depth;
+};
+
static void describe(const char *arg, int last_one)
{
unsigned char sha1[20];
struct commit_list *list;
static int initialized = 0;
struct commit_name *n;
+ struct possible_tag *all_matches, *min_match, *cur_match;
if (get_sha1(arg, sha1))
die("Not a valid object name %s", arg);
}
list = NULL;
+ all_matches = NULL;
+ cur_match = NULL;
commit_list_insert(cmit, &list);
while (list) {
- struct commit *c = pop_most_recent_commit(&list, SEEN);
+ struct commit *c = pop_commit(&list);
n = match(c);
if (n) {
- printf("%s-g%s\n", n->path,
- find_unique_abbrev(cmit->object.sha1, abbrev));
- if (!last_one)
- clear_commit_marks(cmit, SEEN);
- return;
+ struct possible_tag *p = xmalloc(sizeof(*p));
+ p->name = n;
+ p->next = NULL;
+ if (cur_match)
+ cur_match->next = p;
+ else
+ all_matches = p;
+ cur_match = p;
+ } else {
+ struct commit_list *parents = c->parents;
+ while (parents) {
+ struct commit *p = parents->item;
+ parse_commit(p);
+ if (!(p->object.flags & SEEN)) {
+ p->object.flags |= SEEN;
+ insert_by_date(p, &list);
+ }
+ parents = parents->next;
+ }
+ }
+ }
+
+ if (!all_matches)
+ die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
+
+ min_match = NULL;
+ for (cur_match = all_matches; cur_match; cur_match = cur_match->next) {
+ struct rev_info revs;
+ struct commit *tagged = cur_match->name->commit;
+
+ clear_commit_marks(cmit, -1);
+ init_revisions(&revs, NULL);
+ tagged->object.flags |= UNINTERESTING;
+ add_pending_object(&revs, &tagged->object, NULL);
+ add_pending_object(&revs, &cmit->object, NULL);
+
+ prepare_revision_walk(&revs);
+ cur_match->depth = 0;
+ while ((!min_match || cur_match->depth < min_match->depth)
+ && get_revision(&revs))
+ cur_match->depth++;
+ if (!min_match || cur_match->depth < min_match->depth)
+ min_match = cur_match;
+ free_commit_list(revs.commits);
+ }
+ printf("%s-g%s\n", min_match->name->path,
+ find_unique_abbrev(cmit->object.sha1, abbrev));
+
+ if (!last_one) {
+ for (cur_match = all_matches; cur_match; cur_match = min_match) {
+ min_match = cur_match->next;
+ free(cur_match);
}
+ clear_commit_marks(cmit, SEEN);
}
- die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
}
int cmd_describe(int argc, const char **argv, const char *prefix)
/* Note: if ".git/hooks" file exists in the repository being
* re-initialized, /etc/core-git/templates/hooks/update would
- * cause git-init-db to fail here. I think this is sane but
+ * cause git-init to fail here. I think this is sane but
* it means that the set of templates we ship by default, along
* with the way the namespace under .git/ is organized, should
* be really carefully chosen.
}
git_config_set("core.filemode", filemode ? "true" : "false");
- /* Enable logAllRefUpdates if a working tree is attached */
- if (!is_bare_git_dir(git_dir))
+ if (is_bare_repository()) {
+ git_config_set("core.bare", "true");
+ }
+ else {
+ git_config_set("core.bare", "false");
git_config_set("core.logallrefupdates", "true");
+ }
return reinit;
}
static const char prune_packed_usage[] =
"git-prune-packed [-n]";
-static void prune_dir(int i, DIR *dir, char *pathname, int len, int dryrun)
+#define DRY_RUN 01
+#define VERBOSE 02
+
+static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
{
struct dirent *de;
char hex[40];
if (!has_sha1_pack(sha1, NULL))
continue;
memcpy(pathname + len, de->d_name, 38);
- if (dryrun)
+ if (opts & DRY_RUN)
printf("rm -f %s\n", pathname);
else if (unlink(pathname) < 0)
error("unable to unlink %s", pathname);
rmdir(pathname);
}
-void prune_packed_objects(int dryrun)
+void prune_packed_objects(int opts)
{
int i;
static char pathname[PATH_MAX];
sprintf(pathname + len, "%02x/", i);
d = opendir(pathname);
+ if (opts == VERBOSE && (d || i == 255))
+ fprintf(stderr, "Removing unused objects %d%%...\015",
+ ((i+1) * 100) / 256);
if (!d)
continue;
- prune_dir(i, d, pathname, len + 3, dryrun);
+ prune_dir(i, d, pathname, len + 3, opts);
closedir(d);
}
+ if (opts == VERBOSE)
+ fprintf(stderr, "\nDone.\n");
}
int cmd_prune_packed(int argc, const char **argv, const char *prefix)
{
int i;
- int dryrun = 0;
+ int opts = VERBOSE;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (*arg == '-') {
if (!strcmp(arg, "-n"))
- dryrun = 1;
+ opts |= DRY_RUN;
+ else if (!strcmp(arg, "-q"))
+ opts &= ~VERBOSE;
else
usage(prune_packed_usage);
continue;
usage(prune_packed_usage);
}
sync();
- prune_packed_objects(dryrun);
+ prune_packed_objects(opts);
return 0;
}
{
struct commit *commit;
- *it = NULL;
if (is_null_sha1(sha1))
return 1;
commit = lookup_commit_reference_gently(sha1, 1);
if (timestamp < cb->cmd->expire_total)
goto prune;
+ old = new = NULL;
if (cb->cmd->stalefix &&
(!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
goto prune;
- if ((timestamp < cb->cmd->expire_unreachable) &&
- (!cb->ref_commit ||
- (old && !in_merge_bases(old, cb->ref_commit)) ||
- (new && !in_merge_bases(new, cb->ref_commit))))
- goto prune;
+ if (timestamp < cb->cmd->expire_unreachable) {
+ if (!cb->ref_commit)
+ goto prune;
+ if (!old && !is_null_sha1(osha1))
+ old = lookup_commit_reference_gently(osha1, 1);
+ if (!new && !is_null_sha1(nsha1))
+ new = lookup_commit_reference_gently(nsha1, 1);
+ if ((old && !in_merge_bases(old, cb->ref_commit)) ||
+ (new && !in_merge_bases(new, cb->ref_commit)))
+ goto prune;
+ }
if (cb->newlog) {
char sign = (tz < 0) ? '-' : '+';
char *slash;
ret = unlink(name);
+ if (ret && errno == ENOENT)
+ /* The user has removed it from the filesystem by hand */
+ ret = errno = 0;
+
if (!ret && (slash = strrchr(name, '/'))) {
char *n = xstrdup(name);
do {
return 0;
/*
- * Then, unless we used "--cache", remove the filenames from
+ * Then, unless we used "--cached", remove the filenames from
* the workspace. If we fail to remove the first one, we
* abort the "git rm" (but once we've successfully removed
* any file at all, we'll go ahead and commit to it all:
#define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
-extern int is_bare_git_dir(const char *dir);
+extern int is_bare_repository_cfg;
+extern int is_bare_repository(void);
extern const char *get_git_dir(void);
extern char *get_object_directory(void);
extern char *get_refs_directory(void);
extern int read_ref(const char *filename, unsigned char *sha1);
extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
extern int create_symref(const char *ref, const char *refs_heads_master);
-extern int validate_symref(const char *ref);
+extern int validate_headref(const char *ref);
extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
extern int copy_fd(int ifd, int ofd);
extern int read_in_full(int fd, void *buf, size_t count);
-extern void read_or_die(int fd, void *buf, size_t count);
extern int write_in_full(int fd, const void *buf, size_t count);
extern void write_or_die(int fd, const void *buf, size_t count);
extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
return 0;
}
+ if (!strcmp(var, "core.bare")) {
+ is_bare_repository_cfg = git_config_bool(var, value);
+ return 0;
+ }
+
if (!strcmp(var, "core.ignorestat")) {
assume_unchanged = git_config_bool(var, value);
return 0;
store.key = (char*)key;
if (!store_write_section(fd, key) ||
- !store_write_pair(fd, key, value)) {
- ret = write_error();
- goto out_free;
- }
- } else{
+ !store_write_pair(fd, key, value))
+ goto write_err_out;
+ } else {
struct stat st;
char* contents;
int i, copy_begin, copy_end, new_line = 0;
/* write the first part of the config */
if (copy_end > copy_begin) {
- write_in_full(fd, contents + copy_begin,
- copy_end - copy_begin);
- if (new_line)
- write_in_full(fd, "\n", 1);
+ if (write_in_full(fd, contents + copy_begin,
+ copy_end - copy_begin) <
+ copy_end - copy_begin)
+ goto write_err_out;
+ if (new_line &&
+ write_in_full(fd, "\n", 1) != 1)
+ goto write_err_out;
}
copy_begin = store.offset[i];
}
/* write the pair (value == NULL means unset) */
if (value != NULL) {
- if (store.state == START)
- if (!store_write_section(fd, key)) {
- ret = write_error();
- goto out_free;
- }
- if (!store_write_pair(fd, key, value)) {
- ret = write_error();
- goto out_free;
+ if (store.state == START) {
+ if (!store_write_section(fd, key))
+ goto write_err_out;
}
+ if (!store_write_pair(fd, key, value))
+ goto write_err_out;
}
/* write the rest of the config */
if (copy_begin < st.st_size)
- write_in_full(fd, contents + copy_begin,
- st.st_size - copy_begin);
+ if (write_in_full(fd, contents + copy_begin,
+ st.st_size - copy_begin) <
+ st.st_size - copy_begin)
+ goto write_err_out;
munmap(contents, st.st_size);
unlink(config_filename);
free(lock_file);
}
return ret;
+
+write_err_out:
+ ret = write_error();
+ goto out_free;
+
}
int git_config_rename_section(const char *old_name, const char *new_name)
int trust_executable_bit = 1;
int assume_unchanged;
int prefer_symlink_refs;
-int log_all_ref_updates;
+int is_bare_repository_cfg = -1; /* unspecified */
+int log_all_ref_updates = -1; /* unspecified */
int warn_ambiguous_refs = 1;
int repository_format_version;
char *git_commit_encoding;
git_graft_file = getenv(GRAFT_ENVIRONMENT);
if (!git_graft_file)
git_graft_file = xstrdup(git_path("info/grafts"));
- log_all_ref_updates = !is_bare_git_dir(git_dir);
}
-int is_bare_git_dir (const char *dir)
+int is_bare_repository(void)
{
- const char *s;
+ const char *dir, *s;
+ if (0 <= is_bare_repository_cfg)
+ return is_bare_repository_cfg;
+
+ dir = get_git_dir();
if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
return 0;
s = strrchr(dir, '/');
{
struct object *obj = parse_object(sha1);
if (!obj)
- return error("%s: object not found", sha1_to_hex(sha1));
+ return error("%s: object corrupt or missing", sha1_to_hex(sha1));
if (obj->flags & SEEN)
return 0;
obj->flags |= SEEN;
diff
fetch
grep
-init-db
+init
log
merge
mv
or, when resuming [--skip | --resolved]'
. git-sh-setup
set_reflog_action am
+require_work_tree
git var GIT_COMMITTER_IDENT >/dev/null || exit
unless (-d $git_dir) { # initial import
if ($psets[0]{type} eq 'i' || $psets[0]{type} eq 't') {
print "Starting import from $psets[0]{id}\n";
- `git-init-db`;
+ `git-init`;
die $! if $?;
$import = 1;
} else {
USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'
SUBDIRECTORY_OK=Sometimes
. git-sh-setup
+require_work_tree
old_name=HEAD
old=$(git-rev-parse --verify $old_name 2>/dev/null)
+oldbranch=$(git-symbolic-ref $old_name 2>/dev/null)
new=
new_name=
force=
newbranch=
newbranch_log=
merge=
+LF='
+'
while [ "$#" != "0" ]; do
arg="$1"
shift
exit 1
fi
new="$rev"
- new_name="$arg^0"
+ new_name="$arg"
if git-show-ref --verify --quiet -- "refs/heads/$arg"
then
branch="$arg"
# We are switching branches and checking out trees, so
# we *NEED* to be at the toplevel.
-cdup=$(git-rev-parse --show-cdup)
-if test ! -z "$cdup"
-then
- cd "$cdup"
-fi
+cd_to_toplevel
[ -z "$new" ] && new=$old && new_name="$old_name"
-# If we don't have an old branch that we're switching to,
+# If we don't have an existing branch that we're switching to,
# and we don't have a new branch name for the target we
-# are switching to, then we'd better just be checking out
-# what we already had
+# are switching to, then we are detaching our HEAD from any
+# branch. However, if "git checkout HEAD" detaches the HEAD
+# from the current branch, even though that may be logically
+# correct, it feels somewhat funny. More importantly, we do not
+# want "git checkout" nor "git checkout -f" to detach HEAD.
-[ -z "$branch$newbranch" ] &&
- [ "$new" != "$old" ] &&
- die "git checkout: provided reference cannot be checked out directly
+detached=
+detach_warn=
- You need -b to associate a new branch with the wanted checkout. Example:
- git checkout -b <new_branch_name> $arg
-"
+if test -z "$branch$newbranch" && test "$new" != "$old"
+then
+ detached="$new"
+ if test -n "$oldbranch"
+ then
+ detach_warn="warning: you are not on ANY branch anymore.
+If you meant to create a new branch from the commit, you need -b to
+associate a new branch with the wanted checkout. Example:
+ git checkout -b <new_branch_name> $arg"
+ fi
+elif test -z "$oldbranch" && test -n "$branch"
+then
+ # Coming back...
+ if test -z "$force"
+ then
+ git show-ref -d -s | grep "$old" >/dev/null || {
+ echo >&2 \
+"You are not on any branch and switching to branch '$new_name'
+may lose your changes. At this point, you can do one of two things:
+ (1) Decide it is Ok and say 'git checkout -f $new_name';
+ (2) Start a new branch from the current commit, by saying
+ 'git checkout -b <branch-name>'.
+Leaving your HEAD detached; not switching to branch '$new_name'."
+ exit 1;
+ }
+ fi
+fi
if [ "X$old" = X ]
then
- echo "warning: You do not appear to currently be on a branch." >&2
- echo "warning: Forcing checkout of $new_name." >&2
+ echo >&2 "warning: You appear to be on a branch yet to be born."
+ echo >&2 "warning: Forcing checkout of $new_name."
force=1
fi
git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit
branch="$newbranch"
fi
- [ "$branch" ] &&
- GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
+ if test -n "$branch"
+ then
+ GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
+ elif test -n "$detached"
+ then
+ # NEEDSWORK: we would want a command to detach the HEAD
+ # atomically, instead of this handcrafted command sequence.
+ # Perhaps:
+ # git update-ref --detach HEAD $new
+ # or something like that...
+ #
+ echo "$detached" >"$GIT_DIR/HEAD.new" &&
+ mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" ||
+ die "Cannot detach HEAD"
+ if test -n "$detach_warn"
+ then
+ echo >&2 "$detach_warn"
+ fi
+ fi
rm -f "$GIT_DIR/MERGE_HEAD"
else
exit 1
affected are further limited to those that match them.'
SUBDIRECTORY_OK=Yes
. git-sh-setup
+require_work_tree
ignored=
ignoredonly=
GIT_DIR="$D" ;;
*)
GIT_DIR="$D/.git" ;;
-esac && export GIT_DIR && git-init-db ${template+"$template"} || usage
+esac && export GIT_DIR && git-init ${template+"$template"} || usage
if test -n "$reference"
then
USAGE='[-a] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-u] [--amend] [-e] [--author <author>] [[-i | -o] <path>...]'
SUBDIRECTORY_OK=Yes
. git-sh-setup
+require_work_tree
git-rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
################################################################
# Prepare index to have a tree to be committed
-TOP=`git-rev-parse --show-cdup`
-if test -z "$TOP"
-then
- TOP=./
-fi
-
case "$all,$also" in
t,)
save_index &&
(
- cd "$TOP"
- GIT_INDEX_FILE="$NEXT_INDEX"
- export GIT_INDEX_FILE
+ cd_to_toplevel &&
+ GIT_INDEX_FILE="$NEXT_INDEX" &&
+ export GIT_INDEX_FILE &&
git-diff-files --name-only -z |
git-update-index --remove -z --stdin
- )
+ ) || exit
;;
,t)
save_index &&
git-diff-files --name-only -z -- "$@" |
(
- cd "$TOP"
- GIT_INDEX_FILE="$NEXT_INDEX"
- export GIT_INDEX_FILE
+ cd_to_toplevel &&
+ GIT_INDEX_FILE="$NEXT_INDEX" &&
+ export GIT_INDEX_FILE &&
git-update-index --remove -z --stdin
- )
+ ) || exit
;;
,)
case "$#" in
{
void *ret = mmap(start, length, prot, flags, fd, offset);
if (ret == MAP_FAILED) {
+ if (!length)
+ return NULL;
release_pack_memory(length);
ret = mmap(start, length, prot, flags, fd, offset);
if (ret == MAP_FAILED)
my %index; # holds filenames of one index per branch
unless (-d $git_dir) {
- system("git-init-db");
+ system("git-init");
die "Cannot init the GIT db at $git_tree: $?\n" if $?;
system("git-read-tree");
die "Cannot init an empty tree: $?\n" if $?;
sub commit {
if ($branch eq $opt_o && !$index{branch} && !get_headref($branch, $git_dir)) {
# looks like an initial commit
- # use the index primed by git-init-db
+ # use the index primed by git-init
$ENV{GIT_INDEX_FILE} = '.git/index';
$index{$branch} = '.git/index';
} else {
SUBDIRECTORY_OK=Yes
. git-sh-setup
set_reflog_action "fetch $*"
+cd_to_toplevel ;# probably unnecessary...
-TOP=$(git-rev-parse --show-cdup)
-if test ! -z "$TOP"
-then
- cd "$TOP"
-fi
. git-parse-remote
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
esac
}
-case "$update_head_ok" in
-'')
+# updating the current HEAD with git-fetch in a bare
+# repository is always fine.
+if test -z "$update_head_ok" && test $(is_bare_repository) = false
+then
orig_head=$(git-rev-parse --verify HEAD 2>/dev/null)
- ;;
-esac
+fi
# If --tags (and later --heads or --all) is specified, then we are
# not talking about defaults stored in Pull: line of remotes or
USAGE='[-n] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+'
+SUBDIRECTORY_OK=Yes
. git-sh-setup
set_reflog_action "merge $*"
+require_work_tree
+cd_to_toplevel
test -z "$(git ls-files -u)" ||
- die "You are in a middle of conflicted merge."
+ die "You are in the middle of a conflicted merge."
LF='
'
self.gitdir = self.get_single("rev-parse --git-dir")
report(2, "gdir:", self.gitdir)
except:
- die("Not a git repository... did you forget to \"git init-db\" ?")
+ die("Not a git repository... did you forget to \"git init\" ?")
try:
self.cdup = self.get_single("rev-parse --show-cdup")
if self.cdup != "":
USAGE='[-n | --no-summary] [--no-commit] [-s strategy]... [<fetch-options>] <repo> <head>...'
LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.'
+SUBDIRECTORY_OK=Yes
. git-sh-setup
set_reflog_action "pull $*"
+require_work_tree
+cd_to_toplevel
test -z "$(git ls-files -u)" ||
- die "You are in a middle of conflicted merge."
+ die "You are in the middle of a conflicted merge."
strategy_args= no_summary= no_commit= squash=
while case "$#,$1" in 0) break ;; *,-*) ;; *) break ;; esac
/ --> /
D---E---F---G master D---E---F---G master
'
+
+SUBDIRECTORY_OK=Yes
. git-sh-setup
set_reflog_action rebase
+require_work_tree
+cd_to_toplevel
RESOLVEMSG="
When you have resolved this problem run \"git rebase --continue\".
}
add_remote($ARGV[1], $ARGV[2]);
}
-
+else {
+ print STDERR "Usage: git remote\n";
+ print STDERR " git remote add <name> <url>\n";
+ print STDERR " git remote show <name>\n";
+ exit(1);
+}
done
)
fi
- git-prune-packed
+ git-prune-packed $quiet
fi
case "$no_update_info" in
SUBDIRECTORY_OK=Yes
. git-sh-setup
set_reflog_action "reset $*"
+require_work_tree
update= reset_type=--mixed
unset rev
exit
fi
-TOP=$(git-rev-parse --show-cdup)
-if test ! -z "$TOP"
-then
- cd "$TOP"
-fi
+cd_to_toplevel
if test "$reset_type" = "--hard"
then
me=cherry-pick
USAGE='[--edit] [-n] [-r] [-x] <commit-ish>' ;;
* )
- die "What are you talking about?" ;;
+ echo >&2 "What are you talking about?"
+ exit 1 ;;
esac
+
+SUBDIRECTORY_OK=Yes ;# we will cd up
. git-sh-setup
+require_work_tree
+cd_to_toplevel
no_commit=
while case "$#" in 0) break ;; esac
fi
}
+is_bare_repository () {
+ git-repo-config --bool --get core.bare ||
+ case "$GIT_DIR" in
+ .git | */.git) echo false ;;
+ *) echo true ;;
+ esac
+}
+
+cd_to_toplevel () {
+ cdup=$(git-rev-parse --show-cdup)
+ if test ! -z "$cdup"
+ then
+ cd "$cdup" || {
+ echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
+ exit 1
+ }
+ fi
+}
+
+require_work_tree () {
+ test $(is_bare_repository) = false ||
+ die "fatal: $0 cannot be used without a working tree."
+}
+
if [ -z "$LONG_USAGE" ]
then
LONG_USAGE="Usage: $0 $USAGE"
if [ -z "$SUBDIRECTORY_OK" ]
then
: ${GIT_DIR=.git}
- GIT_DIR=$(GIT_DIR="$GIT_DIR" git-rev-parse --git-dir) || exit
+ GIT_DIR=$(GIT_DIR="$GIT_DIR" git-rev-parse --git-dir) || {
+ exit=$?
+ echo >&2 "You need to run this command from the toplevel of the working tree."
+ exit $exit
+ }
else
GIT_DIR=$(git-rev-parse --git-dir) || exit
fi
$SVN_URL = $url;
unless (-d $GIT_DIR) {
- my @init_db = ('init-db');
+ my @init_db = ('init');
push @init_db, "--template=$_template" if defined $_template;
push @init_db, "--shared" if defined $_shared;
command_noisy(@init_db);
my $last_branch;
my $current_rev = $opt_s || 1;
unless(-d $git_dir) {
- system("git-init-db");
+ system("git-init");
die "Cannot init the GIT db at $git_tree: $?\n" if $?;
system("git-read-tree");
die "Cannot init an empty tree: $?\n" if $?;
#define RUN_SETUP (1<<0)
#define USE_PAGER (1<<1)
+/*
+ * require working tree to be present -- anything uses this needs
+ * RUN_SETUP for reading from the configuration file.
+ */
+#define NOT_BARE (1<<2)
static void handle_internal_command(int argc, const char **argv, char **envp)
{
int (*fn)(int, const char **, const char *);
int option;
} commands[] = {
- { "add", cmd_add, RUN_SETUP },
+ { "add", cmd_add, RUN_SETUP | NOT_BARE },
{ "annotate", cmd_annotate, },
{ "apply", cmd_apply },
{ "archive", cmd_archive },
{ "mailinfo", cmd_mailinfo },
{ "mailsplit", cmd_mailsplit },
{ "merge-file", cmd_merge_file },
- { "mv", cmd_mv, RUN_SETUP },
+ { "mv", cmd_mv, RUN_SETUP | NOT_BARE },
{ "name-rev", cmd_name_rev, RUN_SETUP },
{ "pack-objects", cmd_pack_objects, RUN_SETUP },
{ "pickaxe", cmd_blame, RUN_SETUP | USE_PAGER },
{ "rerere", cmd_rerere, RUN_SETUP },
{ "rev-list", cmd_rev_list, RUN_SETUP },
{ "rev-parse", cmd_rev_parse, RUN_SETUP },
- { "rm", cmd_rm, RUN_SETUP },
- { "runstatus", cmd_runstatus, RUN_SETUP },
+ { "rm", cmd_rm, RUN_SETUP | NOT_BARE },
+ { "runstatus", cmd_runstatus, RUN_SETUP | NOT_BARE },
{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
{ "show-branch", cmd_show_branch, RUN_SETUP },
{ "show", cmd_show, RUN_SETUP | USE_PAGER },
prefix = setup_git_directory();
if (p->option & USE_PAGER)
setup_pager();
+ if ((p->option & NOT_BARE) && is_bare_repository())
+ die("%s cannot be used in a bare git directory", cmd);
trace_argv_printf(argv, argc, "trace: built-in: git");
exit(p->fn(argc, argv, prefix));
char buf[48];
int len = snprintf(buf, sizeof(buf), "%s\t%s\n",
report, sha1_to_hex(sha1));
- write_in_full(1, buf, len);
+ write_or_die(1, buf, len);
/*
* Let's just mimic git-unpack-objects here and write
}
}
-static const char *current_index_file = NULL;
-static const char *original_index_file;
-static const char *temporary_index_file;
-static int cache_dirty = 0;
-
-static int flush_cache(void)
-{
- /* flush temporary index */
- struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
- int fd = hold_lock_file_for_update(lock, current_index_file, 1);
- if (write_cache(fd, active_cache, active_nr) ||
- close(fd) || commit_lock_file(lock))
- die ("unable to write %s", current_index_file);
- discard_cache();
- cache_dirty = 0;
- return 0;
-}
-
-static void setup_index(int temp)
-{
- current_index_file = temp ? temporary_index_file: original_index_file;
- if (cache_dirty) {
- discard_cache();
- cache_dirty = 0;
- }
- unlink(temporary_index_file);
- discard_cache();
-}
-
static struct cache_entry *make_cache_entry(unsigned int mode,
const unsigned char *sha1, const char *path, int stage, int refresh)
{
const char *path, int stage, int refresh, int options)
{
struct cache_entry *ce;
- if (!cache_dirty)
- read_cache_from(current_index_file);
- cache_dirty++;
ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
if (!ce)
return error("cache_addinfo failed: %s", strerror(cache_errno));
*/
static int index_only = 0;
-static int git_read_tree(struct tree *tree)
-{
- int rc;
- struct object_list *trees = NULL;
- struct unpack_trees_options opts;
-
- if (cache_dirty)
- die("read-tree with dirty cache");
-
- memset(&opts, 0, sizeof(opts));
- object_list_append(&tree->object, &trees);
- rc = unpack_trees(trees, &opts);
- cache_tree_free(&active_cache_tree);
-
- if (rc == 0)
- cache_dirty = 1;
-
- return rc;
-}
-
static int git_merge_trees(int index_only,
struct tree *common,
struct tree *head,
struct object_list *trees = NULL;
struct unpack_trees_options opts;
- if (!cache_dirty) {
- read_cache_from(current_index_file);
- cache_dirty = 1;
- }
-
memset(&opts, 0, sizeof(opts));
if (index_only)
opts.index_only = 1;
rc = unpack_trees(trees, &opts);
cache_tree_free(&active_cache_tree);
-
- cache_dirty = 1;
-
return rc;
}
+static int unmerged_index(void)
+{
+ int i;
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (ce_stage(ce))
+ return 1;
+ }
+ return 0;
+}
+
static struct tree *git_write_tree(void)
{
struct tree *result = NULL;
- if (cache_dirty) {
- unsigned i;
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- if (ce_stage(ce))
- return NULL;
- }
- } else
- read_cache_from(current_index_file);
+ if (unmerged_index())
+ return NULL;
if (!active_cache_tree)
active_cache_tree = cache_tree();
if (!cache_tree_fully_valid(active_cache_tree) &&
- cache_tree_update(active_cache_tree,
- active_cache, active_nr, 0, 0) < 0)
+ cache_tree_update(active_cache_tree,
+ active_cache, active_nr, 0, 0) < 0)
die("error building trees");
result = lookup_tree(active_cache_tree->sha1);
- flush_cache();
- cache_dirty = 0;
-
return result;
}
int i;
unmerged->strdup_paths = 1;
- if (!cache_dirty) {
- read_cache_from(current_index_file);
- cache_dirty++;
- }
+
for (i = 0; i < active_nr; i++) {
struct path_list_item *item;
struct stage_data *e;
int update_working_directory = !index_only && !no_wd;
if (update_cache) {
- if (!cache_dirty)
- read_cache_from(current_index_file);
- cache_dirty++;
if (remove_file_from_cache(path))
return -1;
}
path_list_clear(&a_by_dst, 0);
path_list_clear(&b_by_dst, 0);
- if (cache_dirty)
- flush_cache();
return clean_merge;
}
} else
die("Fatal merge failure, shouldn't happen.");
- if (cache_dirty)
- flush_cache();
-
return clean_merge;
}
sha1_to_hex(head->object.sha1),
sha1_to_hex(merge->object.sha1));
- *result = git_write_tree();
-
- if (!*result) {
+ if (unmerged_index()) {
struct path_list *entries, *re_head, *re_merge;
int i;
path_list_clear(¤t_file_set, 1);
path_list_clear(re_head, 0);
path_list_clear(entries, 1);
- if (clean || index_only)
- *result = git_write_tree();
- else
- *result = NULL;
- } else {
- clean = 1;
- printf("merging of trees %s and %s resulted in %s\n",
- sha1_to_hex(head->object.sha1),
- sha1_to_hex(merge->object.sha1),
- sha1_to_hex((*result)->object.sha1));
}
+ else
+ clean = 1;
+
+ if (index_only)
+ *result = git_write_tree();
return clean;
}
const char *branch1,
const char *branch2,
int call_depth /* =0 */,
- struct commit *ancestor /* =None */,
+ struct commit_list *ca,
struct commit **result)
{
- struct commit_list *ca = NULL, *iter;
+ struct commit_list *iter;
struct commit *merged_common_ancestors;
struct tree *mrtree;
int clean;
output_commit_title(h1);
output_commit_title(h2);
- if (ancestor)
- commit_list_insert(ancestor, &ca);
- else
- ca = reverse_commit_list(get_merge_bases(h1, h2, 1));
+ if (!ca) {
+ ca = get_merge_bases(h1, h2, 1);
+ ca = reverse_commit_list(ca);
+ }
output("found %u common ancestor(s):", commit_list_count(ca));
for (iter = ca; iter; iter = iter->next)
* merge_trees has always overwritten it: the commited
* "conflicts" were already resolved.
*/
+ discard_cache();
merge(merged_common_ancestors, iter->item,
"Temporary merge branch 1",
"Temporary merge branch 2",
die("merge returned no commit");
}
+ discard_cache();
if (call_depth == 0) {
- setup_index(0 /* $GIT_DIR/index */);
+ read_cache();
index_only = 0;
- } else {
- setup_index(1 /* temporary index */);
- git_read_tree(h1->tree);
+ } else
index_only = 1;
- }
clean = merge_trees(h1->tree, h2->tree, merged_common_ancestors->tree,
branch1, branch2, &mrtree);
- if (!ancestor && (clean || index_only)) {
+ if (index_only) {
*result = make_virtual_commit(mrtree, "merged tree");
commit_list_insert(h1, &(*result)->parents);
commit_list_insert(h2, &(*result)->parents->next);
- } else
- *result = NULL;
-
+ }
return clean;
}
int main(int argc, char *argv[])
{
- static const char *bases[2];
+ static const char *bases[20];
static unsigned bases_count = 0;
int i, clean;
const char *branch1, *branch2;
struct commit *result, *h1, *h2;
+ struct commit_list *ca = NULL;
+ struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+ int index_fd;
git_config(git_default_config); /* core.filemode */
- original_index_file = getenv(INDEX_ENVIRONMENT);
-
- if (!original_index_file)
- original_index_file = xstrdup(git_path("index"));
-
- temporary_index_file = xstrdup(git_path("mrg-rcrsv-tmp-idx"));
if (argc < 4)
die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);
branch2 = better_branch_name(branch2);
printf("Merging %s with %s\n", branch1, branch2);
- if (bases_count == 1) {
- struct commit *ancestor = get_ref(bases[0]);
- clean = merge(h1, h2, branch1, branch2, 0, ancestor, &result);
- } else
- clean = merge(h1, h2, branch1, branch2, 0, NULL, &result);
+ index_fd = hold_lock_file_for_update(lock, get_index_file(), 1);
- if (cache_dirty)
- flush_cache();
+ for (i = 0; i < bases_count; i++) {
+ struct commit *ancestor = get_ref(bases[i]);
+ ca = commit_list_insert(ancestor, &ca);
+ }
+ clean = merge(h1, h2, branch1, branch2, 0, ca, &result);
+
+ if (active_cache_changed &&
+ (write_cache(index_fd, active_cache, active_nr) ||
+ close(index_fd) || commit_lock_file(lock)))
+ die ("unable to write %s", get_index_file());
return clean ? 0: 1;
}
-
-/*
-vim: sw=8 noet
-*/
}
-int validate_symref(const char *path)
+int validate_headref(const char *path)
{
struct stat st;
char *buf, buffer[256];
+ unsigned char sha1[20];
int len, fd;
if (lstat(path, &st) < 0)
/*
* Is it a symbolic ref?
*/
- if (len < 4 || memcmp("ref:", buffer, 4))
+ if (len < 4)
return -1;
- buf = buffer + 4;
- len -= 4;
- while (len && isspace(*buf))
- buf++, len--;
- if (len >= 5 && !memcmp("refs/", buf, 5))
+ if (!memcmp("ref:", buffer, 4)) {
+ buf = buffer + 4;
+ len -= 4;
+ while (len && isspace(*buf))
+ buf++, len--;
+ if (len >= 5 && !memcmp("refs/", buf, 5))
+ return 0;
+ }
+
+ /*
+ * Is this a detached HEAD?
+ */
+ if (!get_sha1_hex(buffer, sha1))
return 0;
+
return -1;
}
return NULL;
if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
- validate_symref("HEAD") == 0) {
+ validate_headref("HEAD") == 0) {
putenv("GIT_DIR=.");
check_repository_format();
return path;
the generic command interface.
While some commands can be executed outside of any context (e.g. 'version'
-or 'init-db'), most operations require a repository context, which in practice
+or 'init'), most operations require a repository context, which in practice
means getting an instance of the Git object using the repository() constructor.
(In the future, we will also get a new_repository() constructor.) All commands
called as methods of the object are then executed in the context of the
if (data &&
!write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) &&
!ce_write(&c, newfd, data, sz))
- ;
+ free(data);
else {
free(data);
return -1;
char *logrec;
const char *committer;
+ if (log_all_ref_updates < 0)
+ log_all_ref_updates = !is_bare_repository();
+
if (log_all_ref_updates &&
(!strncmp(lock->ref_name, "refs/heads/", 11) ||
!strncmp(lock->ref_name, "refs/remotes/", 13))) {
void prepare_revision_walk(struct rev_info *revs)
{
int nr = revs->pending.nr;
- struct object_array_entry *list = revs->pending.objects;
+ struct object_array_entry *e, *list;
+ e = list = revs->pending.objects;
revs->pending.nr = 0;
revs->pending.alloc = 0;
revs->pending.objects = NULL;
while (--nr >= 0) {
- struct commit *commit = handle_commit(revs, list->item, list->name);
+ struct commit *commit = handle_commit(revs, e->item, e->name);
if (commit) {
if (!(commit->object.flags & SEEN)) {
commit->object.flags |= SEEN;
insert_by_date(commit, &revs->commits);
}
}
- list++;
+ e++;
}
+ free(list);
if (revs->no_walk)
return;
* GIT_OBJECT_DIRECTORY environment variable
* - a refs/ directory
* - either a HEAD symlink or a HEAD file that is formatted as
- * a proper "ref:".
+ * a proper "ref:", or a regular file HEAD that has a properly
+ * formatted sha1 object name.
*/
static int is_git_directory(const char *suspect)
{
return 0;
strcpy(path + len, "/HEAD");
- if (validate_symref(path))
+ if (validate_headref(path))
return 0;
return 1;
die("cannot set FD_CLOEXEC");
/* Verify we recognize this pack file format. */
- read_or_die(p->pack_fd, &hdr, sizeof(hdr));
+ if (read_in_full(p->pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
+ die("file %s is far too short to be a packfile", p->pack_name);
if (hdr.hdr_signature != htonl(PACK_SIGNATURE))
die("file %s is not a GIT packfile", p->pack_name);
if (!pack_version_ok(hdr.hdr_version))
num_packed_objects(p));
if (lseek(p->pack_fd, p->pack_size - sizeof(sha1), SEEK_SET) == -1)
die("end of packfile %s is unavailable", p->pack_name);
- read_or_die(p->pack_fd, sha1, sizeof(sha1));
+ if (read_in_full(p->pack_fd, sha1, sizeof(sha1)) != sizeof(sha1))
+ die("packfile %s signature is unavailable", p->pack_name);
idx_sha1 = ((unsigned char *)p->index_base) + p->index_size - 40;
if (hashcmp(sha1, idx_sha1))
die("packfile %s does not match index", p->pack_name);
static int write_buffer(int fd, const void *buf, size_t len)
{
- ssize_t size;
-
- size = write_in_full(fd, buf, len);
- if (!size)
- return error("file write: disk full");
- if (size < 0)
+ if (write_in_full(fd, buf, len) < 0)
return error("file write error (%s)", strerror(errno));
return 0;
}
the tests.
*** t0000-basic.sh ***
- * ok 1: .git/objects should be empty after git-init-db in an empty repo.
+ * ok 1: .git/objects should be empty after git-init in an empty repo.
* ok 2: .git/objects should have 256 subdirectories.
* ok 3: git-update-index without --add should fail adding.
...
use SVN::Core;
use SVN::Repos;
\$SVN::Core::VERSION gt '1.1.0' or exit(42);
-SVN::Repos::create('$svnrepo', undef, undef, undef,
- { 'fs-config' => 'fsfs'});
-"
+system(qw/svnadmin create --fs-type fsfs/, '$svnrepo') == 0 or exit(41);
+" >&3 2>&4
x=$?
if test $x -ne 0
then
if test $x -eq 42; then
err='Perl SVN libraries must be >= 1.1.0'
+ elif test $x -eq 41; then
+ err='svnadmin failed to create fsfs repository'
else
err='Perl SVN libraries not found or unusable, skipping test'
fi
. ./test-lib.sh
################################################################
-# init-db has been done in an empty repository.
+# git-init has been done in an empty repository.
# make sure it is empty.
find .git/objects -type f -print >should-be-empty
test_expect_success \
- '.git/objects should be empty after git-init-db in an empty repo.' \
+ '.git/objects should be empty after git-init in an empty repo.' \
'cmp -s /dev/null should-be-empty'
# also it should have 2 subdirectories; no fan-out anymore, pack, and info.
test ! -f .git/logs/refs/heads/d/e/f'
cat >expect <<EOF
-0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 checkout: Created from master^0
+0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 checkout: Created from master
EOF
test_expect_success \
'git checkout -b g/h/i -l should create a branch and a log' \
git tar-tree initial initial | tar xf - &&
(
- cd initial && git init-db && git add .
+ cd initial && git init && git add .
) &&
git tar-tree second second | tar xf - &&
(
- cd second && git init-db && git add .
+ cd second && git init && git add .
)
'
'unpack without delta' \
"GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
- git-init-db &&
+ git-init &&
git-unpack-objects -n <test-1-${packname_1}.pack &&
git-unpack-objects <test-1-${packname_1}.pack"
'unpack with delta' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
- git-init-db &&
+ git-init &&
git-unpack-objects -n <test-2-${packname_2}.pack &&
git-unpack-objects <test-2-${packname_2}.pack'
'use packed objects' \
'GIT_OBJECT_DIRECTORY=.git2/objects &&
export GIT_OBJECT_DIRECTORY &&
- git-init-db &&
+ git-init &&
cp test-1-${packname_1}.pack test-1-${packname_1}.idx .git2/objects/pack && {
git-diff-tree --root -p $commit &&
while read object
(
mkdir client &&
cd client &&
- git-init-db 2>> log2.txt
+ git-init 2>> log2.txt
)
add A1
mkdir four &&
cd four &&
- git init-db &&
+ git init &&
git fetch .. :track &&
git show-ref --verify refs/tags/anno &&
test_expect_success 'pulling into void' '
mkdir cloned &&
cd cloned &&
- git init-db &&
+ git init &&
git pull ..
'
test_expect_success "Michael Cassar's test case" '
rm -fr .git papers partA &&
- git init-db &&
+ git init &&
mkdir -p papers/unsorted papers/all-papers partA &&
echo a > papers/unsorted/Thesis.pdf &&
echo b > partA/outline.txt &&
test_expect_success "Sergey Vlasov's test case" '
rm -fr .git &&
- git init-db &&
+ git init &&
mkdir ab &&
date >ab.c &&
date >ab/d &&
cd wc &&
echo world >> trunk/readme &&
svn commit -m 'another commit' &&
+ svn up &&
svn mv -m 'rename to thunk' trunk thunk &&
svn up &&
echo goodbye >> thunk/readme &&
repo="$1"
mkdir "$repo"
cd "$repo" || error "Cannot setup test environment"
- "$GIT_EXEC_PATH/git" init-db --template=$GIT_EXEC_PATH/templates/blt/ >/dev/null 2>&1 ||
- error "cannot run git init-db -- have you built things yet?"
+ "$GIT_EXEC_PATH/git" init --template=$GIT_EXEC_PATH/templates/blt/ >/dev/null 2>&1 ||
+ error "cannot run git init -- have you built things yet?"
mv .git/hooks .git/hooks-disabled
cd "$owd"
}
{
char *p = buf;
ssize_t total = 0;
- ssize_t loaded = 0;
while (count > 0) {
- loaded = xread(fd, p, count);
- if (loaded <= 0) {
- if (total)
- return total;
- else
- return loaded;
- }
+ ssize_t loaded = xread(fd, p, count);
+ if (loaded <= 0)
+ return total ? total : loaded;
count -= loaded;
p += loaded;
total += loaded;
return total;
}
-void read_or_die(int fd, void *buf, size_t count)
-{
- ssize_t loaded;
-
- loaded = read_in_full(fd, buf, count);
- if (loaded == 0)
- die("unexpected end of file");
- else if (loaded < 0)
- die("read error (%s)", strerror(errno));
-}
-
int write_in_full(int fd, const void *buf, size_t count)
{
const char *p = buf;
ssize_t total = 0;
- ssize_t written = 0;
while (count > 0) {
- written = xwrite(fd, p, count);
- if (written <= 0) {
- if (total)
- return total;
- else
- return written;
+ size_t written = xwrite(fd, p, count);
+ if (written < 0)
+ return -1;
+ if (!written) {
+ errno = ENOSPC;
+ return -1;
}
count -= written;
p += written;
void write_or_die(int fd, const void *buf, size_t count)
{
- ssize_t written;
-
- written = write_in_full(fd, buf, count);
- if (written == 0)
- die("disk full?");
- else if (written < 0) {
+ if (write_in_full(fd, buf, count) < 0) {
if (errno == EPIPE)
exit(0);
die("write error (%s)", strerror(errno));
int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg)
{
- ssize_t written;
-
- written = write_in_full(fd, buf, count);
- if (written == 0) {
- fprintf(stderr, "%s: disk full?\n", msg);
- return 0;
- }
- else if (written < 0) {
+ if (write_in_full(fd, buf, count) < 0) {
if (errno == EPIPE)
exit(0);
fprintf(stderr, "%s: write error (%s)\n",
int write_or_whine(int fd, const void *buf, size_t count, const char *msg)
{
- ssize_t written;
-
- written = write_in_full(fd, buf, count);
- if (written == 0) {
- fprintf(stderr, "%s: disk full?\n", msg);
- return 0;
- }
- else if (written < 0) {
+ if (write_in_full(fd, buf, count) < 0) {
fprintf(stderr, "%s: write error (%s)\n",
msg, strerror(errno));
return 0;
"\033[31m", /* WT_STATUS_CHANGED: red */
"\033[31m", /* WT_STATUS_UNTRACKED: red */
};
-static const char* use_add_msg = "use \"git add <file>...\" to incrementally add content to commit";
+
+static const char use_add_msg[] =
+"use \"git add <file>...\" to update what will be committed";
+static const char use_add_rm_msg[] =
+"use \"git add/rm <file>...\" to update what will be committed";
+static const char use_add_to_include_msg[] =
+"use \"git add <file>...\" to include in what will be committed";
static int parse_status_slot(const char *var, int offset)
{
struct wt_status *s = data;
int i;
if (q->nr) {
+ const char *msg = use_add_msg;
s->workdir_dirty = 1;
- wt_status_print_header("Changed but not added", use_add_msg);
+ for (i = 0; i < q->nr; i++)
+ if (q->queue[i]->status == DIFF_STATUS_DELETED) {
+ msg = use_add_rm_msg;
+ break;
+ }
+ wt_status_print_header("Changed but not updated", msg);
}
for (i = 0; i < q->nr; i++)
wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
}
if (!shown_header) {
s->workdir_untracked = 1;
- wt_status_print_header("Untracked files", use_add_msg);
+ wt_status_print_header("Untracked files",
+ use_add_to_include_msg);
shown_header = 1;
}
color_printf(color(WT_STATUS_HEADER), "#\t");
unsigned char sha1[20];
s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
- if (s->branch)
+ if (s->branch) {
+ const char *on_what = "On branch ";
+ const char *branch_name = s->branch;
+ if (!strncmp(branch_name, "refs/heads/", 11))
+ branch_name += 11;
+ else if (!strcmp(branch_name, "HEAD")) {
+ branch_name = "";
+ on_what = "Not currently on any branch.";
+ }
color_printf_ln(color(WT_STATUS_HEADER),
- "# On branch %s", s->branch);
+ "# %s%s", on_what, branch_name);
+ }
if (s->is_initial) {
color_printf_ln(color(WT_STATUS_HEADER), "#");
if (s->amend)
printf("# No changes\n");
else if (s->workdir_dirty)
- printf("no changes added to commit (use \"git add\" and/or \"git commit [-a|-i|-o]\")\n");
+ printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
else if (s->workdir_untracked)
printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
else if (s->is_initial)