git-core-*/?*
test-date
test-delta
+test-dump-cache-tree
common-cmds.h
*.tar.gz
*.dsc
NAME
----
-git-rebase - Rebase local commits to new upstream head
+git-rebase - Rebase local commits to a new head
SYNOPSIS
--------
'git-rebase' [--onto <newbase>] <upstream> [<branch>]
+'git-rebase' --continue
+
+'git-rebase' --abort
+
DESCRIPTION
-----------
-git-rebase applies to <upstream> (or optionally to <newbase>) commits
-from <branch> that do not appear in <upstream>. When <branch> is not
-specified it defaults to the current branch (HEAD).
+git-rebase replaces <branch> with a new branch of the same name. When
+the --onto option is provided the new branch starts out with a HEAD equal
+to <newbase>, otherwise it is equal to <upstream>. It then attempts to
+create a new commit for each commit from the original <branch> that does
+not exist in the <upstream> branch.
-When git-rebase is complete, <branch> will be updated to point to the
-newly created line of commit objects, so the previous line will not be
-accessible unless there are other references to it already.
+It is possible that a merge failure will prevent this process from being
+completely automatic. You will have to resolve any such merge failure
+and run `git rebase --continue`. If you can not resolve the merge
+failure, running `git rebase --abort` will restore the original <branch>
+and remove the working files found in the .dotest directory.
+
+Note that if <branch> is not specified on the command line, the currently
+checked out branch is used.
Assume the following history exists and the current branch is "topic":
+------------
A---B---C topic
/
D---E---F---G master
+------------
From this point, the result of either of the following commands:
+
git-rebase master
git-rebase master topic
would be:
+------------
A'--B'--C' topic
/
D---E---F---G master
+------------
While, starting from the same point, the result of either of the following
commands:
would be:
+------------
A'--B'--C' topic
/
D---E---F---G master
+------------
In case of conflict, git-rebase will stop at the first problematic commit
-and leave conflict markers in the tree. After resolving the conflict manually
-and updating the index with the desired resolution, you can continue the
-rebasing process with
+and leave conflict markers in the tree. You can use git diff to locate
+the markers (<<<<<<) and make edits to resolve the conflict. For each
+file you edit, you need to tell git that the conflict has been resolved,
+typically this would be done with
+
+
+ git update-index <filename>
+
+
+After resolving the conflict manually and updating the index with the
+desired resolution, you can continue the rebasing process with
+
+
+ git rebase --continue
- git am --resolved --3way
Alternatively, you can undo the git-rebase with
- git reset --hard ORIG_HEAD
- rm -r .dotest
+
+ git rebase --abort
OPTIONS
-------
<branch>::
Working branch; defaults to HEAD.
+--continue::
+ Restart the rebasing process after having resolved a merge conflict.
+
+--abort::
+ Restore the original branch and abort the rebase operation.
+
+NOTES
+-----
+When you rebase a branch, you are changing its history in a way that
+will cause problems for anyone who already has a copy of the branch
+in their repository and tries to pull updates from you. You should
+understand the implications of using 'git rebase' on a repository that
+you share.
+
+When the git rebase command is run, it will first execute a "pre-rebase"
+hook if one exists. You can use this hook to do sanity checks and
+reject the rebase if it isn't appropriate. Please see the template
+pre-rebase hook script for an example.
+
+You must be in the top directory of your project to start (or continue)
+a rebase. Upon completion, <branch> will be the current branch.
+
Author
------
Written by Junio C Hamano <junkio@cox.net>
'git-repo-config' [type] --get-all name [value_regex]
'git-repo-config' [type] --unset name [value_regex]
'git-repo-config' [type] --unset-all name [value_regex]
+'git-repo-config' -l | --list
DESCRIPTION
-----------
--unset-all::
Remove all matching lines from .git/config.
+-l, --list::
+ List all variables set in .git/config.
+
EXAMPLE
-------
-l::
Cause the logical variables to be listed. In addition, all the
variables of the git configuration file .git/config are listed
- as well.
+ as well. (However, the configuration variables listing functionality
+ is deprecated in favor of `git-repo-config -l`.)
EXAMPLE
--------
diffcore-delta.o log-tree.o
LIB_OBJS = \
- blob.o commit.o connect.o csum-file.o \
+ blob.o commit.o connect.o csum-file.o cache-tree.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
object.o pack-check.o patch-delta.o path.o pkt-line.o \
quote.o read-cache.o refs.o run-command.o \
test-delta$X: test-delta.c diff-delta.o patch-delta.o
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^ -lz
+test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS)
+ $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+
check:
for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
*/
#include <fnmatch.h>
#include "cache.h"
+#include "cache-tree.h"
#include "quote.h"
#include "blob.h"
if (write_index) {
if (remove_file_from_cache(patch->old_name) < 0)
die("unable to remove %s from index", patch->old_name);
+ cache_tree_invalidate_path(active_cache_tree, patch->old_name);
}
unlink(patch->old_name);
}
if (!mode)
mode = S_IFREG | 0644;
- create_one_file(path, mode, buf, size);
+ create_one_file(path, mode, buf, size);
add_index_file(path, mode, buf, size);
+ cache_tree_invalidate_path(active_cache_tree, path);
}
static void write_out_one_result(struct patch *patch)
*
* Builtin help-related commands (help, usage, version)
*/
+#include <sys/ioctl.h>
#include "cache.h"
#include "builtin.h"
#include "exec_cmd.h"
#include "diff.h"
#include "revision.h"
#include "log-tree.h"
+#include "builtin.h"
static int cmd_log_wc(int argc, const char **argv, char **envp,
struct rev_info *rev)
rev.diffopt.recursive = 1;
return cmd_log_wc(argc, argv, envp, &rev);
}
+
+int cmd_format_patch(int argc, const char **argv, char **envp)
+{
+ struct commit *commit;
+ struct commit **list = NULL;
+ struct rev_info rev;
+ int nr = 0;
+
+ init_revisions(&rev);
+ rev.commit_format = CMIT_FMT_EMAIL;
+ rev.verbose_header = 1;
+ rev.diff = 1;
+ rev.diffopt.with_raw = 0;
+ rev.diffopt.with_stat = 1;
+ rev.combine_merges = 0;
+ rev.ignore_merges = 1;
+ rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+ argc = setup_revisions(argc, argv, &rev, "HEAD");
+
+ prepare_revision_walk(&rev);
+ while ((commit = get_revision(&rev)) != NULL) {
+ nr++;
+ list = realloc(list, nr * sizeof(list[0]));
+ list[nr - 1] = commit;
+ }
+ while (0 <= --nr) {
+ int shown;
+ commit = list[nr];
+ shown = log_tree_commit(&rev, commit);
+ free(commit->buffer);
+ commit->buffer = NULL;
+ if (shown)
+ printf("-- \n%s\n\n", git_version_string);
+ }
+ free(list);
+ return 0;
+}
+
extern int cmd_whatchanged(int argc, const char **argv, char **envp);
extern int cmd_show(int argc, const char **argv, char **envp);
extern int cmd_log(int argc, const char **argv, char **envp);
+extern int cmd_format_patch(int argc, const char **argv, char **envp);
#endif
--- /dev/null
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+#define DEBUG 0
+
+struct cache_tree *cache_tree(void)
+{
+ struct cache_tree *it = xcalloc(1, sizeof(struct cache_tree));
+ it->entry_count = -1;
+ return it;
+}
+
+void cache_tree_free(struct cache_tree **it_p)
+{
+ int i;
+ struct cache_tree *it = *it_p;
+
+ if (!it)
+ return;
+ for (i = 0; i < it->subtree_nr; i++)
+ if (it->down[i])
+ cache_tree_free(&it->down[i]->cache_tree);
+ free(it->down);
+ free(it);
+ *it_p = NULL;
+}
+
+static int subtree_name_cmp(const char *one, int onelen,
+ const char *two, int twolen)
+{
+ if (onelen < twolen)
+ return -1;
+ if (twolen < onelen)
+ return 1;
+ return memcmp(one, two, onelen);
+}
+
+static int subtree_pos(struct cache_tree *it, const char *path, int pathlen)
+{
+ struct cache_tree_sub **down = it->down;
+ int lo, hi;
+ lo = 0;
+ hi = it->subtree_nr;
+ while (lo < hi) {
+ int mi = (lo + hi) / 2;
+ struct cache_tree_sub *mdl = down[mi];
+ int cmp = subtree_name_cmp(path, pathlen,
+ mdl->name, mdl->namelen);
+ if (!cmp)
+ return mi;
+ if (cmp < 0)
+ hi = mi;
+ else
+ lo = mi + 1;
+ }
+ return -lo-1;
+}
+
+static struct cache_tree_sub *find_subtree(struct cache_tree *it,
+ const char *path,
+ int pathlen,
+ int create)
+{
+ struct cache_tree_sub *down;
+ int pos = subtree_pos(it, path, pathlen);
+ if (0 <= pos)
+ return it->down[pos];
+ if (!create)
+ return NULL;
+
+ pos = -pos-1;
+ if (it->subtree_alloc <= it->subtree_nr) {
+ it->subtree_alloc = alloc_nr(it->subtree_alloc);
+ it->down = xrealloc(it->down, it->subtree_alloc *
+ sizeof(*it->down));
+ }
+ it->subtree_nr++;
+
+ down = xmalloc(sizeof(*down) + pathlen + 1);
+ down->cache_tree = NULL;
+ down->namelen = pathlen;
+ memcpy(down->name, path, pathlen);
+ down->name[pathlen] = 0;
+
+ if (pos < it->subtree_nr)
+ memmove(it->down + pos + 1,
+ it->down + pos,
+ sizeof(down) * (it->subtree_nr - pos - 1));
+ it->down[pos] = down;
+ return down;
+}
+
+struct cache_tree_sub *cache_tree_sub(struct cache_tree *it, const char *path)
+{
+ int pathlen = strlen(path);
+ return find_subtree(it, path, pathlen, 1);
+}
+
+void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
+{
+ /* a/b/c
+ * ==> invalidate self
+ * ==> find "a", have it invalidate "b/c"
+ * a
+ * ==> invalidate self
+ * ==> if "a" exists as a subtree, remove it.
+ */
+ const char *slash;
+ int namelen;
+ struct cache_tree_sub *down;
+
+ if (!it)
+ return;
+ slash = strchr(path, '/');
+ it->entry_count = -1;
+ if (!slash) {
+ int pos;
+ namelen = strlen(path);
+ pos = subtree_pos(it, path, namelen);
+ if (0 <= pos) {
+ cache_tree_free(&it->down[pos]->cache_tree);
+ free(it->down[pos]);
+ /* 0 1 2 3 4 5
+ * ^ ^subtree_nr = 6
+ * pos
+ * move 4 and 5 up one place (2 entries)
+ * 2 = 6 - 3 - 1 = subtree_nr - pos - 1
+ */
+ memmove(it->down+pos, it->down+pos+1,
+ sizeof(struct cache_tree_sub *) *
+ (it->subtree_nr - pos - 1));
+ it->subtree_nr--;
+ }
+ return;
+ }
+ namelen = slash - path;
+ down = find_subtree(it, path, namelen, 0);
+ if (down)
+ cache_tree_invalidate_path(down->cache_tree, slash + 1);
+}
+
+static int verify_cache(struct cache_entry **cache,
+ int entries)
+{
+ int i, funny;
+
+ /* Verify that the tree is merged */
+ funny = 0;
+ for (i = 0; i < entries; i++) {
+ struct cache_entry *ce = cache[i];
+ if (ce_stage(ce)) {
+ if (10 < ++funny) {
+ fprintf(stderr, "...\n");
+ break;
+ }
+ fprintf(stderr, "%s: unmerged (%s)\n",
+ ce->name, sha1_to_hex(ce->sha1));
+ }
+ }
+ if (funny)
+ return -1;
+
+ /* Also verify that the cache does not have path and path/file
+ * at the same time. At this point we know the cache has only
+ * stage 0 entries.
+ */
+ funny = 0;
+ for (i = 0; i < entries - 1; i++) {
+ /* path/file always comes after path because of the way
+ * the cache is sorted. Also path can appear only once,
+ * which means conflicting one would immediately follow.
+ */
+ const char *this_name = cache[i]->name;
+ const char *next_name = cache[i+1]->name;
+ int this_len = strlen(this_name);
+ if (this_len < strlen(next_name) &&
+ strncmp(this_name, next_name, this_len) == 0 &&
+ next_name[this_len] == '/') {
+ if (10 < ++funny) {
+ fprintf(stderr, "...\n");
+ break;
+ }
+ fprintf(stderr, "You have both %s and %s\n",
+ this_name, next_name);
+ }
+ }
+ if (funny)
+ return -1;
+ return 0;
+}
+
+static void discard_unused_subtrees(struct cache_tree *it)
+{
+ struct cache_tree_sub **down = it->down;
+ int nr = it->subtree_nr;
+ int dst, src;
+ for (dst = src = 0; src < nr; src++) {
+ struct cache_tree_sub *s = down[src];
+ if (s->used)
+ down[dst++] = s;
+ else {
+ cache_tree_free(&s->cache_tree);
+ free(s);
+ it->subtree_nr--;
+ }
+ }
+}
+
+int cache_tree_fully_valid(struct cache_tree *it)
+{
+ int i;
+ if (!it)
+ return 0;
+ if (it->entry_count < 0 || !has_sha1_file(it->sha1))
+ return 0;
+ for (i = 0; i < it->subtree_nr; i++) {
+ if (!cache_tree_fully_valid(it->down[i]->cache_tree))
+ return 0;
+ }
+ return 1;
+}
+
+static int update_one(struct cache_tree *it,
+ struct cache_entry **cache,
+ int entries,
+ const char *base,
+ int baselen,
+ int missing_ok)
+{
+ unsigned long size, offset;
+ char *buffer;
+ int i;
+
+ if (0 <= it->entry_count && has_sha1_file(it->sha1))
+ return it->entry_count;
+
+ /*
+ * We first scan for subtrees and update them; we start by
+ * marking existing subtrees -- the ones that are unmarked
+ * should not be in the result.
+ */
+ for (i = 0; i < it->subtree_nr; i++)
+ it->down[i]->used = 0;
+
+ /*
+ * Find the subtrees and update them.
+ */
+ for (i = 0; i < entries; i++) {
+ struct cache_entry *ce = cache[i];
+ struct cache_tree_sub *sub;
+ const char *path, *slash;
+ int pathlen, sublen, subcnt;
+
+ path = ce->name;
+ pathlen = ce_namelen(ce);
+ if (pathlen <= baselen || memcmp(base, path, baselen))
+ break; /* at the end of this level */
+
+ slash = strchr(path + baselen, '/');
+ if (!slash)
+ continue;
+ /*
+ * a/bbb/c (base = a/, slash = /c)
+ * ==>
+ * path+baselen = bbb/c, sublen = 3
+ */
+ sublen = slash - (path + baselen);
+ sub = find_subtree(it, path + baselen, sublen, 1);
+ if (!sub->cache_tree)
+ sub->cache_tree = cache_tree();
+ subcnt = update_one(sub->cache_tree,
+ cache + i, entries - i,
+ path,
+ baselen + sublen + 1,
+ missing_ok);
+ i += subcnt - 1;
+ sub->used = 1;
+ }
+
+ discard_unused_subtrees(it);
+
+ /*
+ * Then write out the tree object for this level.
+ */
+ size = 8192;
+ buffer = xmalloc(size);
+ offset = 0;
+
+ for (i = 0; i < entries; i++) {
+ struct cache_entry *ce = cache[i];
+ struct cache_tree_sub *sub;
+ const char *path, *slash;
+ int pathlen, entlen;
+ const unsigned char *sha1;
+ unsigned mode;
+
+ path = ce->name;
+ pathlen = ce_namelen(ce);
+ if (pathlen <= baselen || memcmp(base, path, baselen))
+ break; /* at the end of this level */
+
+ slash = strchr(path + baselen, '/');
+ if (slash) {
+ entlen = slash - (path + baselen);
+ sub = find_subtree(it, path + baselen, entlen, 0);
+ if (!sub)
+ die("cache-tree.c: '%.*s' in '%s' not found",
+ entlen, path + baselen, path);
+ i += sub->cache_tree->entry_count - 1;
+ sha1 = sub->cache_tree->sha1;
+ mode = S_IFDIR;
+ }
+ else {
+ sha1 = ce->sha1;
+ mode = ntohl(ce->ce_mode);
+ entlen = pathlen - baselen;
+ }
+ if (!missing_ok && !has_sha1_file(sha1))
+ return error("invalid object %s", sha1_to_hex(sha1));
+
+ if (!ce->ce_mode)
+ continue; /* entry being removed */
+
+ if (size < offset + entlen + 100) {
+ size = alloc_nr(offset + entlen + 100);
+ buffer = xrealloc(buffer, size);
+ }
+ offset += sprintf(buffer + offset,
+ "%o %.*s", mode, entlen, path + baselen);
+ buffer[offset++] = 0;
+ memcpy(buffer + offset, sha1, 20);
+ offset += 20;
+
+#if DEBUG
+ fprintf(stderr, "cache-tree %o %.*s\n",
+ mode, entlen, path + baselen);
+#endif
+ }
+
+ write_sha1_file(buffer, offset, tree_type, it->sha1);
+ free(buffer);
+ it->entry_count = i;
+#if DEBUG
+ fprintf(stderr, "cache-tree (%d ent, %d subtree) %s\n",
+ it->entry_count, it->subtree_nr,
+ sha1_to_hex(it->sha1));
+#endif
+ return i;
+}
+
+int cache_tree_update(struct cache_tree *it,
+ struct cache_entry **cache,
+ int entries,
+ int missing_ok)
+{
+ int i;
+ i = verify_cache(cache, entries);
+ if (i)
+ return i;
+ i = update_one(it, cache, entries, "", 0, missing_ok);
+ if (i < 0)
+ return i;
+ return 0;
+}
+
+static void *write_one(struct cache_tree *it,
+ char *path,
+ int pathlen,
+ char *buffer,
+ unsigned long *size,
+ unsigned long *offset)
+{
+ int i;
+
+ /* One "cache-tree" entry consists of the following:
+ * path (NUL terminated)
+ * entry_count, subtree_nr ("%d %d\n")
+ * tree-sha1 (missing if invalid)
+ * subtree_nr "cache-tree" entries for subtrees.
+ */
+ if (*size < *offset + pathlen + 100) {
+ *size = alloc_nr(*offset + pathlen + 100);
+ buffer = xrealloc(buffer, *size);
+ }
+ *offset += sprintf(buffer + *offset, "%.*s%c%d %d\n",
+ pathlen, path, 0,
+ it->entry_count, it->subtree_nr);
+
+#if DEBUG
+ if (0 <= it->entry_count)
+ fprintf(stderr, "cache-tree <%.*s> (%d ent, %d subtree) %s\n",
+ pathlen, path, it->entry_count, it->subtree_nr,
+ sha1_to_hex(it->sha1));
+ else
+ fprintf(stderr, "cache-tree <%.*s> (%d subtree) invalid\n",
+ pathlen, path, it->subtree_nr);
+#endif
+
+ if (0 <= it->entry_count) {
+ memcpy(buffer + *offset, it->sha1, 20);
+ *offset += 20;
+ }
+ for (i = 0; i < it->subtree_nr; i++) {
+ struct cache_tree_sub *down = it->down[i];
+ if (i) {
+ struct cache_tree_sub *prev = it->down[i-1];
+ if (subtree_name_cmp(down->name, down->namelen,
+ prev->name, prev->namelen) <= 0)
+ die("fatal - unsorted cache subtree");
+ }
+ buffer = write_one(down->cache_tree, down->name, down->namelen,
+ buffer, size, offset);
+ }
+ return buffer;
+}
+
+void *cache_tree_write(struct cache_tree *root, unsigned long *size_p)
+{
+ char path[PATH_MAX];
+ unsigned long size = 8192;
+ char *buffer = xmalloc(size);
+
+ *size_p = 0;
+ path[0] = 0;
+ return write_one(root, path, 0, buffer, &size, size_p);
+}
+
+static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
+{
+ const char *buf = *buffer;
+ unsigned long size = *size_p;
+ struct cache_tree *it;
+ int i, subtree_nr;
+
+ it = NULL;
+ /* skip name, but make sure name exists */
+ while (size && *buf) {
+ size--;
+ buf++;
+ }
+ if (!size)
+ goto free_return;
+ buf++; size--;
+ it = cache_tree();
+ if (sscanf(buf, "%d %d\n", &it->entry_count, &subtree_nr) != 2)
+ goto free_return;
+ while (size && *buf && *buf != '\n') {
+ size--;
+ buf++;
+ }
+ if (!size)
+ goto free_return;
+ buf++; size--;
+ if (0 <= it->entry_count) {
+ if (size < 20)
+ goto free_return;
+ memcpy(it->sha1, buf, 20);
+ buf += 20;
+ size -= 20;
+ }
+
+#if DEBUG
+ if (0 <= it->entry_count)
+ fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n",
+ *buffer, it->entry_count, subtree_nr,
+ sha1_to_hex(it->sha1));
+ else
+ fprintf(stderr, "cache-tree <%s> (%d subtrees) invalid\n",
+ *buffer, subtree_nr);
+#endif
+
+ /*
+ * Just a heuristic -- we do not add directories that often but
+ * we do not want to have to extend it immediately when we do,
+ * hence +2.
+ */
+ it->subtree_alloc = subtree_nr + 2;
+ it->down = xcalloc(it->subtree_alloc, sizeof(struct cache_tree_sub *));
+ for (i = 0; i < subtree_nr; i++) {
+ /* read each subtree */
+ struct cache_tree *sub;
+ struct cache_tree_sub *subtree;
+ const char *name = buf;
+
+ sub = read_one(&buf, &size);
+ if (!sub)
+ goto free_return;
+ subtree = cache_tree_sub(it, name);
+ subtree->cache_tree = sub;
+ }
+ if (subtree_nr != it->subtree_nr)
+ die("cache-tree: internal error");
+ *buffer = buf;
+ *size_p = size;
+ return it;
+
+ free_return:
+ cache_tree_free(&it);
+ return NULL;
+}
+
+struct cache_tree *cache_tree_read(const char *buffer, unsigned long size)
+{
+ if (buffer[0])
+ return NULL; /* not the whole tree */
+ return read_one(&buffer, &size);
+}
--- /dev/null
+#ifndef CACHE_TREE_H
+#define CACHE_TREE_H
+
+struct cache_tree;
+struct cache_tree_sub {
+ struct cache_tree *cache_tree;
+ int namelen;
+ int used;
+ char name[FLEX_ARRAY];
+};
+
+struct cache_tree {
+ int entry_count; /* negative means "invalid" */
+ unsigned char sha1[20];
+ int subtree_nr;
+ int subtree_alloc;
+ struct cache_tree_sub **down;
+};
+
+struct cache_tree *cache_tree(void);
+void cache_tree_free(struct cache_tree **);
+void cache_tree_invalidate_path(struct cache_tree *, const char *);
+struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *);
+
+void *cache_tree_write(struct cache_tree *root, unsigned long *size_p);
+struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
+
+int cache_tree_fully_valid(struct cache_tree *);
+int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int);
+
+#endif
extern struct cache_entry **active_cache;
extern unsigned int active_nr, active_alloc, active_cache_changed;
+extern struct cache_tree *active_cache_tree;
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
#include "cache.h"
#include "strbuf.h"
#include "quote.h"
+#include "cache-tree.h"
#define CHECKOUT_ALL 4
static const char *prefix;
return CMIT_FMT_FULL;
if (!strcmp(arg, "=fuller"))
return CMIT_FMT_FULLER;
+ if (!strcmp(arg, "=email"))
+ return CMIT_FMT_EMAIL;
if (!strcmp(arg, "=oneline"))
return CMIT_FMT_ONELINE;
die("invalid --pretty format");
time = strtoul(date, &date, 10);
tz = strtol(date, NULL, 10);
+ if (fmt == CMIT_FMT_EMAIL) {
+ what = "From";
+ filler = "";
+ }
ret = sprintf(buf, "%s: %.*s%.*s\n", what,
(fmt == CMIT_FMT_FULLER) ? 4 : 0,
filler, namelen, line);
case CMIT_FMT_MEDIUM:
ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
break;
+ case CMIT_FMT_EMAIL:
+ ret += sprintf(buf + ret, "Date: %s\n", show_date(time, tz));
+ break;
case CMIT_FMT_FULLER:
ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
break;
return ret;
}
-static int is_empty_line(const char *line, int len)
+static int is_empty_line(const char *line, int *len_p)
{
+ int len = *len_p;
while (len && isspace(line[len-1]))
len--;
+ *len_p = len;
return !len;
}
struct commit_list *parent = commit->parents;
int offset;
- if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next)
+ if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
+ !parent || !parent->next)
return 0;
offset = sprintf(buf, "Merge:");
{
int hdr = 1, body = 0;
unsigned long offset = 0;
- int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4;
+ int indent = 4;
int parents_shown = 0;
const char *msg = commit->buffer;
+ const char *subject = NULL;
+
+ if (fmt == CMIT_FMT_EMAIL)
+ subject = "Subject: [PATCH] ";
+ if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
+ indent = 0;
for (;;) {
const char *line = msg;
if (hdr) {
if (linelen == 1) {
hdr = 0;
- if (fmt != CMIT_FMT_ONELINE)
+ if ((fmt != CMIT_FMT_ONELINE) && !subject)
buf[offset++] = '\n';
continue;
}
continue;
}
- if (is_empty_line(line, linelen)) {
+ if (is_empty_line(line, &linelen)) {
if (!body)
continue;
+ if (subject)
+ continue;
if (fmt == CMIT_FMT_SHORT)
break;
} else {
body = 1;
}
+ if (subject) {
+ int slen = strlen(subject);
+ memcpy(buf + offset, subject, slen);
+ offset += slen;
+ }
memset(buf + offset, ' ', indent);
memcpy(buf + offset + indent, line, linelen);
offset += linelen + indent;
+ buf[offset++] = '\n';
if (fmt == CMIT_FMT_ONELINE)
break;
+ subject = NULL;
}
while (offset && isspace(buf[offset-1]))
offset--;
CMIT_FMT_FULL,
CMIT_FMT_FULLER,
CMIT_FMT_ONELINE,
+ CMIT_FMT_EMAIL,
CMIT_FMT_UNSPECIFIED,
};
#ifndef DELTA_H
#define DELTA_H
-/* handling of delta buffers */
-extern void *diff_delta(void *from_buf, unsigned long from_size,
- void *to_buf, unsigned long to_size,
- unsigned long *delta_size, unsigned long max_size);
-extern void *patch_delta(void *src_buf, unsigned long src_size,
- void *delta_buf, unsigned long delta_size,
+/* opaque object for delta index */
+struct delta_index;
+
+/*
+ * create_delta_index: compute index data from given buffer
+ *
+ * This returns a pointer to a struct delta_index that should be passed to
+ * subsequent create_delta() calls, or to free_delta_index(). A NULL pointer
+ * is returned on failure. The given buffer must not be freed nor altered
+ * before free_delta_index() is called. The returned pointer must be freed
+ * using free_delta_index().
+ */
+extern struct delta_index *
+create_delta_index(const void *buf, unsigned long bufsize);
+
+/*
+ * free_delta_index: free the index created by create_delta_index()
+ */
+extern void free_delta_index(struct delta_index *index);
+
+/*
+ * create_delta: create a delta from given index for the given buffer
+ *
+ * This function may be called multiple times with different buffers using
+ * the same delta_index pointer. If max_delta_size is non-zero and the
+ * resulting delta is to be larger than max_delta_size then NULL is returned.
+ * On success, a non-NULL pointer to the buffer with the delta data is
+ * returned and *delta_size is updated with its size. The returned buffer
+ * must be freed by the caller.
+ */
+extern void *
+create_delta(const struct delta_index *index,
+ const void *buf, unsigned long bufsize,
+ unsigned long *delta_size, unsigned long max_delta_size);
+
+/*
+ * diff_delta: create a delta from source buffer to target buffer
+ *
+ * If max_delta_size is non-zero and the resulting delta is to be larger
+ * than max_delta_size then NULL is returned. On success, a non-NULL
+ * pointer to the buffer with the delta data is returned and *delta_size is
+ * updated with its size. The returned buffer must be freed by the caller.
+ */
+static inline void *
+diff_delta(const void *src_buf, unsigned long src_bufsize,
+ const void *trg_buf, unsigned long trg_bufsize,
+ unsigned long *delta_size, unsigned long max_delta_size)
+{
+ struct delta_index *index = create_delta_index(src_buf, src_bufsize);
+ if (index) {
+ void *delta = create_delta(index, trg_buf, trg_bufsize,
+ delta_size, max_delta_size);
+ free_delta_index(index);
+ return delta;
+ }
+ return NULL;
+}
+
+/*
+ * patch_delta: recreate target buffer given source buffer and delta data
+ *
+ * On success, a non-NULL pointer to the target buffer is returned and
+ * *trg_bufsize is updated with its size. On failure a NULL pointer is
+ * returned. The returned buffer must be freed by the caller.
+ */
+extern void *patch_delta(const void *src_buf, unsigned long src_size,
+ const void *delta_buf, unsigned long delta_size,
unsigned long *dst_size);
/* the smallest possible delta size is 4 bytes */
/*
* This must be called twice on the delta data buffer, first to get the
- * expected reference buffer size, and again to get the result buffer size.
+ * expected source buffer size, and again to get the target buffer size.
*/
static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
const unsigned char *top)
/* block size: min = 16, max = 64k, power of 2 */
#define BLK_SIZE 16
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
+/* maximum hash entry list for the same hash bucket */
+#define HASH_LIMIT 64
#define GR_PRIME 0x9e370001
#define HASH(v, shift) (((unsigned int)(v) * GR_PRIME) >> (shift))
-struct index {
+struct index_entry {
const unsigned char *ptr;
unsigned int val;
- struct index *next;
+ struct index_entry *next;
};
-static struct index ** delta_index(const unsigned char *buf,
- unsigned long bufsize,
- unsigned long trg_bufsize,
- unsigned int *hash_shift)
+struct delta_index {
+ const void *src_buf;
+ unsigned long src_size;
+ unsigned int hash_shift;
+ struct index_entry *hash[0];
+};
+
+struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
{
- unsigned int i, hsize, hshift, hlimit, entries, *hash_count;
- const unsigned char *data;
- struct index *entry, **hash;
+ unsigned int i, hsize, hshift, entries, *hash_count;
+ const unsigned char *data, *buffer = buf;
+ struct delta_index *index;
+ struct index_entry *entry, **hash;
void *mem;
+ if (!buf || !bufsize)
+ return NULL;
+
/* determine index hash size */
entries = bufsize / BLK_SIZE;
hsize = entries / 4;
for (i = 4; (1 << i) < hsize && i < 31; i++);
hsize = 1 << i;
hshift = 32 - i;
- *hash_shift = hshift;
/* allocate lookup index */
- mem = malloc(hsize * sizeof(*hash) + entries * sizeof(*entry));
+ mem = malloc(sizeof(*index) +
+ sizeof(*hash) * hsize +
+ sizeof(*entry) * entries);
if (!mem)
return NULL;
+ index = mem;
+ mem = index + 1;
hash = mem;
- entry = mem + hsize * sizeof(*hash);
+ mem = hash + hsize;
+ entry = mem;
+
+ index->src_buf = buf;
+ index->src_size = bufsize;
+ index->hash_shift = hshift;
memset(hash, 0, hsize * sizeof(*hash));
/* allocate an array to count hash entries */
hash_count = calloc(hsize, sizeof(*hash_count));
if (!hash_count) {
- free(hash);
+ free(index);
return NULL;
}
/* then populate the index */
- data = buf + entries * BLK_SIZE - BLK_SIZE;
- while (data >= buf) {
+ data = buffer + entries * BLK_SIZE - BLK_SIZE;
+ while (data >= buffer) {
unsigned int val = adler32(0, data, BLK_SIZE);
i = HASH(val, hshift);
entry->ptr = data;
* bucket that would bring us to O(m*n) computing costs (m and n
* corresponding to reference and target buffer sizes).
*
- * The more the target buffer is large, the more it is important to
- * have small entry lists for each hash buckets. With such a limit
- * the cost is bounded to something more like O(m+n).
- */
- hlimit = (1 << 26) / trg_bufsize;
- if (hlimit < 4*BLK_SIZE)
- hlimit = 4*BLK_SIZE;
-
- /*
- * Now make sure none of the hash buckets has more entries than
+ * Make sure none of the hash buckets has more entries than
* we're willing to test. Otherwise we cull the entry list
* uniformly to still preserve a good repartition across
* the reference buffer.
*/
for (i = 0; i < hsize; i++) {
- if (hash_count[i] < hlimit)
+ if (hash_count[i] < HASH_LIMIT)
continue;
entry = hash[i];
do {
- struct index *keep = entry;
- int skip = hash_count[i] / hlimit / 2;
+ struct index_entry *keep = entry;
+ int skip = hash_count[i] / HASH_LIMIT / 2;
do {
entry = entry->next;
} while(--skip && entry);
}
free(hash_count);
- return hash;
+ return index;
+}
+
+void free_delta_index(struct delta_index *index)
+{
+ free(index);
}
/* provide the size of the copy opcode given the block offset and size */
/* the maximum size for any opcode */
#define MAX_OP_SIZE COPYOP_SIZE(0xffffffff, 0xffffffff)
-void *diff_delta(void *from_buf, unsigned long from_size,
- void *to_buf, unsigned long to_size,
- unsigned long *delta_size,
- unsigned long max_size)
+void *
+create_delta(const struct delta_index *index,
+ const void *trg_buf, unsigned long trg_size,
+ unsigned long *delta_size, unsigned long max_size)
{
unsigned int i, outpos, outsize, hash_shift;
int inscnt;
const unsigned char *ref_data, *ref_top, *data, *top;
unsigned char *out;
- struct index *entry, **hash;
- if (!from_size || !to_size)
- return NULL;
- hash = delta_index(from_buf, from_size, to_size, &hash_shift);
- if (!hash)
+ if (!trg_buf || !trg_size)
return NULL;
outpos = 0;
if (max_size && outsize >= max_size)
outsize = max_size + MAX_OP_SIZE + 1;
out = malloc(outsize);
- if (!out) {
- free(hash);
+ if (!out)
return NULL;
- }
-
- ref_data = from_buf;
- ref_top = from_buf + from_size;
- data = to_buf;
- top = to_buf + to_size;
/* store reference buffer size */
- out[outpos++] = from_size;
- from_size >>= 7;
- while (from_size) {
- out[outpos - 1] |= 0x80;
- out[outpos++] = from_size;
- from_size >>= 7;
+ i = index->src_size;
+ while (i >= 0x80) {
+ out[outpos++] = i | 0x80;
+ i >>= 7;
}
+ out[outpos++] = i;
/* store target buffer size */
- out[outpos++] = to_size;
- to_size >>= 7;
- while (to_size) {
- out[outpos - 1] |= 0x80;
- out[outpos++] = to_size;
- to_size >>= 7;
+ i = trg_size;
+ while (i >= 0x80) {
+ out[outpos++] = i | 0x80;
+ i >>= 7;
}
+ out[outpos++] = i;
+ ref_data = index->src_buf;
+ ref_top = ref_data + index->src_size;
+ data = trg_buf;
+ top = trg_buf + trg_size;
+ hash_shift = index->hash_shift;
inscnt = 0;
while (data < top) {
unsigned int moff = 0, msize = 0;
- if (data + BLK_SIZE <= top) {
- unsigned int val = adler32(0, data, BLK_SIZE);
- i = HASH(val, hash_shift);
- for (entry = hash[i]; entry; entry = entry->next) {
- const unsigned char *ref = entry->ptr;
- const unsigned char *src = data;
- unsigned int ref_size = ref_top - ref;
- if (entry->val != val)
- continue;
- if (ref_size > top - src)
- ref_size = top - src;
- if (ref_size > 0x10000)
- ref_size = 0x10000;
- if (ref_size <= msize)
- break;
- while (ref_size-- && *src++ == *ref)
- ref++;
- if (msize < ref - entry->ptr) {
- /* this is our best match so far */
- msize = ref - entry->ptr;
- moff = entry->ptr - ref_data;
- }
+ struct index_entry *entry;
+ unsigned int val = adler32(0, data, BLK_SIZE);
+ i = HASH(val, hash_shift);
+ for (entry = index->hash[i]; entry; entry = entry->next) {
+ const unsigned char *ref = entry->ptr;
+ const unsigned char *src = data;
+ unsigned int ref_size = ref_top - ref;
+ if (entry->val != val)
+ continue;
+ if (ref_size > top - src)
+ ref_size = top - src;
+ if (ref_size > 0x10000)
+ ref_size = 0x10000;
+ if (ref_size <= msize)
+ break;
+ while (ref_size-- && *src++ == *ref)
+ ref++;
+ if (msize < ref - entry->ptr) {
+ /* this is our best match so far */
+ msize = ref - entry->ptr;
+ moff = entry->ptr - ref_data;
}
}
out = realloc(out, outsize);
if (!out) {
free(tmp);
- free(hash);
return NULL;
}
}
if (inscnt)
out[outpos - inscnt - 1] = inscnt;
- free(hash);
*delta_size = outpos;
return out;
}
int main(int argc, const char **argv)
{
struct rev_info rev;
- int match_missing = 0;
int cached = 0;
int i;
static void builtin_diffstat(const char *name_a, const char *name_b,
struct diff_filespec *one,
struct diff_filespec *two,
- struct diffstat_t *diffstat)
+ struct diffstat_t *diffstat,
+ int complete_rewrite)
{
mmfile_t mf1, mf2;
struct diffstat_file *data;
data->is_unmerged = 1;
return;
}
-
+ if (complete_rewrite) {
+ diff_populate_filespec(one, 0);
+ diff_populate_filespec(two, 0);
+ data->deleted = count_lines(one->data, one->size);
+ data->added = count_lines(two->data, two->size);
+ return;
+ }
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
{
const char *name;
const char *other;
+ int complete_rewrite = 0;
if (DIFF_PAIR_UNMERGED(p)) {
/* unmerged */
- builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat);
+ builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, 0);
return;
}
diff_fill_sha1_info(p->one);
diff_fill_sha1_info(p->two);
- builtin_diffstat(name, other, p->one, p->two, diffstat);
+ if (p->status == DIFF_STATUS_MODIFIED && p->score)
+ complete_rewrite = 1;
+ builtin_diffstat(name, other, p->one, p->two, diffstat, complete_rewrite);
}
void diff_setup(struct diff_options *options)
--- /dev/null
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+static void dump_cache_tree(struct cache_tree *it, const char *pfx)
+{
+ int i;
+ if (!it)
+ return;
+ if (it->entry_count < 0)
+ printf("%-40s %s (%d subtrees)\n", "invalid", pfx,
+ it->subtree_nr);
+ else
+ printf("%s %s (%d entries, %d subtrees)\n",
+ sha1_to_hex(it->sha1),
+ pfx, it->entry_count, it->subtree_nr);
+ for (i = 0; i < it->subtree_nr; i++) {
+ char path[PATH_MAX];
+ struct cache_tree_sub *down = it->down[i];
+ sprintf(path, "%s%.*s/", pfx, down->namelen, down->name);
+ dump_cache_tree(down->cache_tree, path);
+ }
+}
+
+int main(int ac, char **av)
+{
+ if (read_cache() < 0)
+ die("unable to read index file");
+ dump_cache_tree(active_cache_tree, "");
+ return 0;
+}
#include "tag.h"
#include "refs.h"
#include "pack.h"
+#include "cache-tree.h"
#define REACHABLE 0x0001
return 0;
}
+static int fsck_cache_tree(struct cache_tree *it)
+{
+ int i;
+ int err = 0;
+
+ if (0 <= it->entry_count) {
+ struct object *obj = parse_object(it->sha1);
+ if (obj->type != tree_type)
+ err |= objerror(obj, "non-tree in cache-tree");
+ }
+ for (i = 0; i < it->subtree_nr; i++)
+ err |= fsck_cache_tree(it->down[i]->cache_tree);
+ return err;
+}
+
int main(int argc, char **argv)
{
int i, heads;
obj->used = 1;
mark_reachable(obj, REACHABLE);
}
+ if (active_cache_tree)
+ fsck_cache_tree(active_cache_tree);
}
check_connectivity();
$log->debug("Temporary directory is '$TEMP_DIR'");
# if we are called with a pserver argument,
-# deal with the authentication cat before entereing the
+# deal with the authentication cat before entering the
# main loop
if (@ARGV && $ARGV[0] eq 'pserver') {
my $line = <STDIN>; chomp $line;
{
chomp;
- # Check to see if we've seen this method, and call appropiate function.
+ # Check to see if we've seen this method, and call appropriate function.
if ( /^([\w-]+)(?:\s+(.*))?$/ and defined($methods->{$1}) )
{
# use the $methods hash to call the appropriate sub for this command
return 0;
}
- my @gitvars = `git-var -l`;
+ my @gitvars = `git-repo-config -l`;
if ($?) {
- print "E problems executing git-var on the server -- this is not a git repository or the PATH is not set correcly.\n";
+ print "E problems executing git-repo-config on the server -- this is not a git repository or the PATH is not set correctly.\n";
print "E \n";
- print "error 1 - problem executing git-var\n";
+ print "error 1 - problem executing git-repo-config\n";
return 0;
}
foreach my $line ( @gitvars )
sub req_Validresponses
{
my ( $cmd, $data ) = @_;
- $log->debug("req_Validrepsonses : $data");
+ $log->debug("req_Validresponses : $data");
# TODO : re-enable this, currently it's not particularly useful
#$state->{validresponses} = [ split /\s+/, $data ];
argsplit("update");
#
- # It may just be a client exploring the available heads/modukles
+ # It may just be a client exploring the available heads/modules
# in that case, list them as top level directories and leave it
# at that. Eclipse uses this technique to offer you a list of
# projects (heads in this case) to checkout.
}
# This method takes a file name, and returns ( $dirpart, $filepart ) which
-# refers to the directory porition and the file portion of the filename
+# refers to the directory portion and the file portion of the filename
# respectively
sub filenamesplit
{
=head2 new
Creates a new log object, optionally you can specify a filename here to
-indicate the file to log to. If no log file is specified, you can specifiy one
+indicate the file to log to. If no log file is specified, you can specify one
later with method setfile, or indicate you no longer want logging with method
nofile.
=head2 safe_pipe_capture
-an alterative to `command` that allows input to be passed as an array
+an alternative to `command` that allows input to be passed as an array
to work around shell problems with weird characters in arguments
=cut
if [ -n "$GIT_SSL_NO_VERIFY" ]; then
curl_extra_args="-k"
fi
- remote_name_quoted=$(perl -e '
+ max_depth=5
+ depth=0
+ head="ref: $remote_name"
+ while (expr "z$head" : "zref:" && expr $depth \< $max_depth) >/dev/null
+ do
+ remote_name_quoted=$(perl -e '
my $u = $ARGV[0];
+ $u =~ s/^ref:\s*//;
$u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
print "$u";
- ' "$remote_name")
- head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted") &&
+ ' "$head")
+ head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted")
+ depth=$( expr \( $depth + 1 \) )
+ done
expr "z$head" : "z$_x40\$" >/dev/null ||
- die "Failed to fetch $remote_name from $remote"
+ die "Failed to fetch $remote_name from $remote"
echo >&2 Fetching "$remote_name from $remote" using http
git-http-fetch -v -a "$head" "$remote/" || exit
;;
#
USAGE='[--onto <newbase>] <upstream> [<branch>]'
-LONG_USAGE='git-rebase applies to <upstream> (or optionally to <newbase>) commits
-from <branch> that do not appear in <upstream>. When <branch> is not
-specified it defaults to the current branch (HEAD).
-
-When git-rebase is complete, <branch> will be updated to point to the
-newly created line of commit objects, so the previous line will not be
-accessible unless there are other references to it already.
-
-Assuming the following history:
-
- A---B---C topic
- /
- D---E---F---G master
-
-The result of the following command:
-
- git-rebase --onto master~1 master topic
-
- would be:
-
- A'\''--B'\''--C'\'' topic
- /
- D---E---F---G master
+LONG_USAGE='git-rebase replaces <branch> with a new branch of the
+same name. When the --onto option is provided the new branch starts
+out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
+It then attempts to create a new commit for each commit from the original
+<branch> that does not exist in the <upstream> branch.
+
+It is possible that a merge failure will prevent this process from being
+completely automatic. You will have to resolve any such merge failure
+and run git-rebase --continue. If you can not resolve the merge failure,
+running git-rebase --abort will restore the original <branch> and remove
+the working files found in the .dotest directory.
+
+Note that if <branch> is not specified on the command line, the
+currently checked out branch is used. You must be in the top
+directory of your project to start (or continue) a rebase.
+
+Example: git-rebase master~1 topic
+
+ A---B---C topic A'\''--B'\''--C'\'' topic
+ / --> /
+ D---E---F---G master D---E---F---G master
'
-
. git-sh-setup
unset newbase
while case "$#" in 0) break ;; esac
do
case "$1" in
+ --continue)
+ diff=$(git-diff-files)
+ case "$diff" in
+ ?*) echo "You must edit all merge conflicts and then"
+ echo "mark them as resolved using git update-index"
+ exit 1
+ ;;
+ esac
+ git am --resolved --3way
+ exit
+ ;;
+ --abort)
+ [ -d .dotest ] || die "No rebase in progress?"
+ git reset --hard ORIG_HEAD
+ rm -r .dotest
+ exit
+ ;;
--onto)
test 2 -le "$#" || usage
newbase="$2"
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
-#include <sys/ioctl.h>
#include "git-compat-util.h"
#include "exec_cmd.h"
{ "log", cmd_log },
{ "whatchanged", cmd_whatchanged },
{ "show", cmd_show },
+ { "fmt-patch", cmd_format_patch },
};
int i;
/*
* Print header line of header..
*/
- printf("%s%s",
- opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
- diff_unique_abbrev(commit->object.sha1, abbrev_commit));
- if (parent)
- printf(" (from %s)", diff_unique_abbrev(parent->object.sha1, abbrev_commit));
- putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+
+ if (opt->commit_format == CMIT_FMT_EMAIL)
+ printf("From %s Thu Apr 7 15:13:13 2005\n",
+ sha1_to_hex(commit->object.sha1));
+ else {
+ printf("%s%s",
+ opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
+ diff_unique_abbrev(commit->object.sha1, abbrev_commit));
+ if (parent)
+ printf(" (from %s)",
+ diff_unique_abbrev(parent->object.sha1,
+ abbrev_commit));
+ putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+ }
/*
* And then the pretty-printed message itself
int log_tree_commit(struct rev_info *opt, struct commit *commit)
{
struct log_info log;
+ int shown;
log.commit = commit;
log.parent = NULL;
opt->loginfo = &log;
- if (!log_tree_diff(opt, commit, &log) && opt->loginfo && opt->always_show_header) {
+ shown = log_tree_diff(opt, commit, &log);
+ if (!shown && opt->loginfo && opt->always_show_header) {
log.parent = NULL;
show_log(opt, opt->loginfo, "");
+ shown = 1;
}
opt->loginfo = NULL;
- return 0;
+ return shown;
}
#include <string.h>
#include "delta.h"
-void *patch_delta(void *src_buf, unsigned long src_size,
- void *delta_buf, unsigned long delta_size,
+void *patch_delta(const void *src_buf, unsigned long src_size,
+ const void *delta_buf, unsigned long delta_size,
unsigned long *dst_size)
{
const unsigned char *data, *top;
* Copyright (C) Linus Torvalds, 2005
*/
#include "cache.h"
+#include "cache-tree.h"
+
+/* Index extensions.
+ *
+ * The first letter should be 'A'..'Z' for extensions that are not
+ * necessary for a correct operation (i.e. optimization data).
+ * When new extensions are added that _needs_ to be understood in
+ * order to correctly interpret the index file, pick character that
+ * is outside the range, to cause the reader to abort.
+ */
+
+#define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
+#define CACHE_EXT_TREE 0x54524545 /* "TREE" */
struct cache_entry **active_cache = NULL;
static time_t index_file_timestamp;
unsigned int active_nr = 0, active_alloc = 0, active_cache_changed = 0;
+struct cache_tree *active_cache_tree = NULL;
+
/*
* This only updates the "non-critical" parts of the directory
* cache, ie the parts that aren't tracked by GIT, and only used
return 0;
}
+static int read_index_extension(const char *ext, void *data, unsigned long sz)
+{
+ switch (CACHE_EXT(ext)) {
+ case CACHE_EXT_TREE:
+ active_cache_tree = cache_tree_read(data, sz);
+ break;
+ default:
+ if (*ext < 'A' || 'Z' < *ext)
+ return error("index uses %.4s extension, which we do not understand",
+ ext);
+ fprintf(stderr, "ignoring %.4s extension\n", ext);
+ break;
+ }
+ return 0;
+}
+
int read_cache(void)
{
int fd, i;
active_cache[i] = ce;
}
index_file_timestamp = st.st_mtime;
+ while (offset <= size - 20 - 8) {
+ /* After an array of active_nr index entries,
+ * there can be arbitrary number of extended
+ * sections, each of which is prefixed with
+ * extension name (4-byte) and section length
+ * in 4-byte network byte order.
+ */
+ unsigned long extsize;
+ memcpy(&extsize, map + offset + 4, 4);
+ extsize = ntohl(extsize);
+ if (read_index_extension(map + offset,
+ map + offset + 8, extsize) < 0)
+ goto unmap;
+ offset += 8;
+ offset += extsize;
+ }
return active_nr;
unmap:
return 0;
}
+static int write_index_ext_header(SHA_CTX *context, int fd,
+ unsigned long ext, unsigned long sz)
+{
+ ext = htonl(ext);
+ sz = htonl(sz);
+ if ((ce_write(context, fd, &ext, 4) < 0) ||
+ (ce_write(context, fd, &sz, 4) < 0))
+ return -1;
+ return 0;
+}
+
static int ce_flush(SHA_CTX *context, int fd)
{
unsigned int left = write_buffer_len;
if (ce_write(&c, newfd, ce, ce_size(ce)) < 0)
return -1;
}
+
+ /* Write extension data here */
+ if (active_cache_tree) {
+ unsigned long sz;
+ void *data = cache_tree_write(active_cache_tree, &sz);
+ if (data &&
+ !write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) &&
+ !ce_write(&c, newfd, data, sz))
+ ;
+ else {
+ free(data);
+ return -1;
+ }
+ }
return ce_flush(&c, newfd);
}
#include "object.h"
#include "tree.h"
+#include "cache-tree.h"
#include <sys/time.h>
#include <signal.h>
die("Entry '%s' not uptodate. Cannot merge.", ce->name);
}
+static void invalidate_ce_path(struct cache_entry *ce)
+{
+ if (ce)
+ cache_tree_invalidate_path(active_cache_tree, ce->name);
+}
+
static int merged_entry(struct cache_entry *merge, struct cache_entry *old)
{
merge->ce_flags |= htons(CE_UPDATE);
*merge = *old;
} else {
verify_uptodate(old);
+ invalidate_ce_path(old);
}
}
merge->ce_flags &= ~htons(CE_STAGEMASK);
verify_uptodate(old);
ce->ce_mode = 0;
add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
+ invalidate_ce_path(ce);
return 1;
}
return error("Cannot do a oneway merge of %d trees",
merge_size);
- if (!a)
+ if (!a) {
+ invalidate_ce_path(old);
return 0;
+ }
if (old && same(old, a)) {
return keep_entry(old);
}
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce)) {
deleted++;
+ invalidate_ce_path(ce);
continue;
}
if (deleted)
return deleted;
}
+static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
+{
+ struct tree_entry_list *ent;
+ int cnt;
+
+ memcpy(it->sha1, tree->object.sha1, 20);
+ for (cnt = 0, ent = tree->entries; ent; ent = ent->next) {
+ if (!ent->directory)
+ cnt++;
+ else {
+ struct cache_tree_sub *sub;
+ struct tree *subtree = (struct tree *)ent->item.tree;
+ if (!subtree->object.parsed)
+ parse_tree(subtree);
+ sub = cache_tree_sub(it, ent->name);
+ sub->cache_tree = cache_tree();
+ prime_cache_tree_rec(sub->cache_tree, subtree);
+ cnt += sub->cache_tree->entry_count;
+ }
+ }
+ it->entry_count = cnt;
+}
+
+static void prime_cache_tree(void)
+{
+ struct tree *tree = (struct tree *)trees->item;
+ if (!tree)
+ return;
+ active_cache_tree = cache_tree();
+ prime_cache_tree_rec(active_cache_tree, tree);
+
+}
+
static const char read_tree_usage[] = "git-read-tree (<sha> | -m [--aggressive] [-u | -i] <sha1> [<sha2> [<sha3>]])";
static struct cache_file cache_file;
fn = twoway_merge;
break;
case 3:
- fn = threeway_merge;
- break;
default:
fn = threeway_merge;
+ cache_tree_free(&active_cache_tree);
break;
}
}
unpack_trees(fn);
+
+ /*
+ * When reading only one tree (either the most basic form,
+ * "-m ent" or "--reset ent" form), we can obtain a fully
+ * valid cache-tree because the index must match exactly
+ * what came from the tree.
+ */
+ if (trees->item && (!merge || (stage == 2))) {
+ cache_tree_free(&active_cache_tree);
+ prime_cache_tree();
+ }
+
if (write_cache(newfd, active_cache, active_nr) ||
commit_index_file(&cache_file))
die("unable to write new index file");
#include <regex.h>
static const char git_config_set_usage[] =
-"git-repo-config [ --bool | --int ] [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]";
+"git-repo-config [ --bool | --int ] [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list";
static char* key = NULL;
static char* value = NULL;
static int seen = 0;
static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
+static int show_all_config(const char *key_, const char *value_)
+{
+ if (value_)
+ printf("%s=%s\n", key_, value_);
+ else
+ printf("%s\n", key_);
+ return 0;
+}
+
static int show_config(const char* key_, const char* value_)
{
if (value_ == NULL)
}
}
- i = git_config(show_config);
+ git_config(show_config);
if (value) {
printf("%s\n", value);
free(value);
argv++;
}
+ if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
+ return git_config(show_all_config);
+
switch (argc) {
case 2:
return get_value(argv[1], NULL);
'no diff after checkout and git-update-index --refresh.' \
'git-diff-files >current && cmp -s current /dev/null'
+################################################################
+P=087704a96baf1c2d1c869a8b084481e121c88b5b
+test_expect_success \
+ 'git-commit-tree records the correct tree in a commit.' \
+ 'commit0=$(echo NO | git-commit-tree $P) &&
+ tree=$(git show --pretty=raw $commit0 |
+ sed -n -e "s/^tree //p" -e "/^author /q") &&
+ test "z$tree" = "z$P"'
+
+test_expect_success \
+ 'git-commit-tree records the correct parent in a commit.' \
+ 'commit1=$(echo NO | git-commit-tree $P -p $commit0) &&
+ parent=$(git show --pretty=raw $commit1 |
+ sed -n -e "s/^parent //p" -e "/^author /q") &&
+ test "z$commit0" = "z$parent"'
+
+test_expect_success \
+ 'git-commit-tree omits duplicated parent in a commit.' \
+ 'commit2=$(echo NO | git-commit-tree $P -p $commit0 -p $commit0) &&
+ parent=$(git show --pretty=raw $commit2 |
+ sed -n -e "s/^parent //p" -e "/^author /q" |
+ sort -u) &&
+ test "z$commit0" = "z$parent" &&
+ numparent=$(git show --pretty=raw $commit2 |
+ sed -n -e "s/^parent //p" -e "/^author /q" |
+ wc -l) &&
+ test $numparent = 1'
+
test_done
#include "cache.h"
#include "strbuf.h"
#include "quote.h"
+#include "cache-tree.h"
#include "tree-walk.h"
/*
active_cache[pos]->ce_flags &= ~htons(CE_VALID);
break;
}
+ cache_tree_invalidate_path(active_cache_tree, path);
active_cache_changed = 1;
return 0;
}
struct stat st;
status = lstat(path, &st);
+
+ /* We probably want to do this in remove_file_from_cache() and
+ * add_cache_entry() instead...
+ */
+ cache_tree_invalidate_path(active_cache_tree, path);
+
if (status < 0 || S_ISDIR(st.st_mode)) {
/* When we used to have "path" and now we want to add
* "path/file", we need a way to remove "path" before
return error("%s: cannot add to the index - missing --add option?",
path);
report("add '%s'", path);
+ cache_tree_invalidate_path(active_cache_tree, path);
return 0;
}
default:
goto fail;
}
+ cache_tree_invalidate_path(active_cache_tree, path);
active_cache_changed = 1;
report("chmod %cx '%s'", flip, path);
return;
die("Unable to mark file %s", path);
return;
}
+ cache_tree_invalidate_path(active_cache_tree, path);
if (force_remove) {
if (remove_file_from_cache(p))
free(path_name);
continue;
}
+ cache_tree_invalidate_path(active_cache_tree, path_name);
if (!mode) {
/* mode == 0 means there is no such path -- remove */
goto free_return;
}
+ cache_tree_invalidate_path(active_cache_tree, path);
remove_file_from_cache(path);
if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
error("%s: cannot add our version to the index.", path);
*/
#include "cache.h"
#include "tree.h"
+#include "cache-tree.h"
static int missing_ok = 0;
-static int check_valid_sha1(unsigned char *sha1)
-{
- int ret;
-
- /* If we were anal, we'd check that the sha1 of the contents actually matches */
- ret = has_sha1_file(sha1);
- if (ret == 0)
- perror(sha1_file_name(sha1));
- return ret ? 0 : -1;
-}
-
-static int write_tree(struct cache_entry **cachep, int maxentries, const char *base, int baselen, unsigned char *returnsha1)
-{
- unsigned char subdir_sha1[20];
- unsigned long size, offset;
- char *buffer;
- int nr;
-
- /* Guess at some random initial size */
- size = 8192;
- buffer = xmalloc(size);
- offset = 0;
-
- nr = 0;
- while (nr < maxentries) {
- struct cache_entry *ce = cachep[nr];
- const char *pathname = ce->name, *filename, *dirname;
- int pathlen = ce_namelen(ce), entrylen;
- unsigned char *sha1;
- unsigned int mode;
-
- /* Did we hit the end of the directory? Return how many we wrote */
- if (baselen >= pathlen || memcmp(base, pathname, baselen))
- break;
-
- sha1 = ce->sha1;
- mode = ntohl(ce->ce_mode);
-
- /* Do we have _further_ subdirectories? */
- filename = pathname + baselen;
- dirname = strchr(filename, '/');
- if (dirname) {
- int subdir_written;
-
- subdir_written = write_tree(cachep + nr, maxentries - nr, pathname, dirname-pathname+1, subdir_sha1);
- nr += subdir_written;
-
- /* Now we need to write out the directory entry into this tree.. */
- mode = S_IFDIR;
- pathlen = dirname - pathname;
-
- /* ..but the directory entry doesn't count towards the total count */
- nr--;
- sha1 = subdir_sha1;
- }
-
- if (!missing_ok && check_valid_sha1(sha1) < 0)
- exit(1);
-
- entrylen = pathlen - baselen;
- if (offset + entrylen + 100 > size) {
- size = alloc_nr(offset + entrylen + 100);
- buffer = xrealloc(buffer, size);
- }
- offset += sprintf(buffer + offset, "%o %.*s", mode, entrylen, filename);
- buffer[offset++] = 0;
- memcpy(buffer + offset, sha1, 20);
- offset += 20;
- nr++;
- }
-
- write_sha1_file(buffer, offset, tree_type, returnsha1);
- free(buffer);
- return nr;
-}
-
static const char write_tree_usage[] = "git-write-tree [--missing-ok]";
+static struct cache_file cache_file;
+
int main(int argc, char **argv)
{
- int i, funny;
- int entries;
- unsigned char sha1[20];
-
+ int entries, was_valid, newfd;
+
setup_git_directory();
+ newfd = hold_index_file_for_update(&cache_file, get_index_file());
entries = read_cache();
if (argc == 2) {
if (!strcmp(argv[1], "--missing-ok"))
if (entries < 0)
die("git-write-tree: error reading cache");
- /* Verify that the tree is merged */
- funny = 0;
- for (i = 0; i < entries; i++) {
- struct cache_entry *ce = active_cache[i];
- if (ce_stage(ce)) {
- if (10 < ++funny) {
- fprintf(stderr, "...\n");
- break;
- }
- fprintf(stderr, "%s: unmerged (%s)\n", ce->name, sha1_to_hex(ce->sha1));
+ if (!active_cache_tree)
+ active_cache_tree = cache_tree();
+
+ was_valid = cache_tree_fully_valid(active_cache_tree);
+ if (!was_valid) {
+ if (cache_tree_update(active_cache_tree,
+ active_cache, active_nr,
+ missing_ok) < 0)
+ die("git-write-tree: error building trees");
+ if (0 <= newfd) {
+ if (!write_cache(newfd, active_cache, active_nr))
+ commit_index_file(&cache_file);
}
- }
- if (funny)
- die("git-write-tree: not able to write tree");
-
- /* Also verify that the cache does not have path and path/file
- * at the same time. At this point we know the cache has only
- * stage 0 entries.
- */
- funny = 0;
- for (i = 0; i < entries - 1; i++) {
- /* path/file always comes after path because of the way
- * the cache is sorted. Also path can appear only once,
- * which means conflicting one would immediately follow.
+ /* Not being able to write is fine -- we are only interested
+ * in updating the cache-tree part, and if the next caller
+ * ends up using the old index with unupdated cache-tree part
+ * it misses the work we did here, but that is just a
+ * performance penalty and not a big deal.
*/
- const char *this_name = active_cache[i]->name;
- const char *next_name = active_cache[i+1]->name;
- int this_len = strlen(this_name);
- if (this_len < strlen(next_name) &&
- strncmp(this_name, next_name, this_len) == 0 &&
- next_name[this_len] == '/') {
- if (10 < ++funny) {
- fprintf(stderr, "...\n");
- break;
- }
- fprintf(stderr, "You have both %s and %s\n",
- this_name, next_name);
- }
}
- if (funny)
- die("git-write-tree: not able to write tree");
-
- /* Ok, write it out */
- if (write_tree(active_cache, entries, "", 0, sha1) != entries)
- die("git-write-tree: internal error");
- printf("%s\n", sha1_to_hex(sha1));
+ printf("%s\n", sha1_to_hex(active_cache_tree->sha1));
return 0;
}