diff options
-rw-r--r-- | Documentation/RelNotes-1.5.1.2.txt | 27 | ||||
-rw-r--r-- | Documentation/git-archive.txt | 5 | ||||
-rw-r--r-- | Documentation/git-cherry-pick.txt | 2 | ||||
-rw-r--r-- | Documentation/git-config.txt | 24 | ||||
-rw-r--r-- | Documentation/git-tar-tree.txt | 2 | ||||
-rw-r--r-- | Documentation/tutorial.txt | 2 | ||||
-rwxr-xr-x | GIT-VERSION-GEN | 2 | ||||
-rw-r--r-- | builtin-config.c | 2 | ||||
-rw-r--r-- | builtin-fetch--tool.c | 84 | ||||
-rw-r--r-- | builtin-rev-list.c | 4 | ||||
-rw-r--r-- | builtin-update-index.c | 2 | ||||
-rw-r--r-- | contrib/emacs/git.el | 42 | ||||
-rwxr-xr-x | contrib/gitview/gitview | 260 | ||||
-rw-r--r-- | contrib/hooks/update-paranoid | 284 | ||||
-rw-r--r-- | convert-objects.c | 2 | ||||
-rw-r--r-- | decorate.c | 1 | ||||
-rwxr-xr-x | git-clone.sh | 2 | ||||
-rwxr-xr-x | git-fetch.sh | 42 | ||||
-rw-r--r-- | merge-recursive.c | 119 | ||||
-rw-r--r-- | perl/Makefile.PL | 2 | ||||
-rwxr-xr-x | t/t3030-merge-recursive.sh | 104 | ||||
-rwxr-xr-x | t/t5502-quickfetch.sh | 89 |
22 files changed, 905 insertions, 198 deletions
diff --git a/Documentation/RelNotes-1.5.1.2.txt b/Documentation/RelNotes-1.5.1.2.txt index f58268f6be..d88456306c 100644 --- a/Documentation/RelNotes-1.5.1.2.txt +++ b/Documentation/RelNotes-1.5.1.2.txt @@ -1,4 +1,4 @@ -GIT v1.5.1.2 Release Notes (draft) +GIT v1.5.1.2 Release Notes ========================== Fixes since v1.5.1.1 @@ -6,6 +6,11 @@ Fixes since v1.5.1.1 * Bugfixes + - "git clone" over http from a repository that has lost the + loose refs by running "git pack-refs" were broken (a code to + deal with this was added to "git fetch" in v1.5.0, but it + was missing from "git clone"). + - "git diff a/ b/" incorrectly fell in "diff between two filesystem objects" codepath, when the user most likely wanted to limit the extent of output to two tracked @@ -24,14 +29,22 @@ Fixes since v1.5.1.1 - git-blame on a very long working tree path had buffer overrun problem. + - git-apply did not like to be fed two patches in a row that created + and then modified the same file. + + - git-svn was confused when a non-project was stored directly under + trunk/, branches/ and tags/. + + - git-svn wants the Error.pm module that was at least as new + as what we ship as part of git; install ours in our private + installation location if the one on the system is older. + + - An earlier update to command line integer parameter parser was + botched and made 'update-index --cacheinfo' completely useless. + + * Documentation updates - Various documentation updates from J. Bruce Fields, Frank Lichtenheld, Alex Riesen and others. Andrew Ruder started a war on undocumented options. - ---- -exec >/var/tmp/1 -O=v1.5.1.1-31-g0220f1e -echo O=`git describe refs/heads/maint` -git shortlog --no-merges $O..refs/heads/maint diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt index 8d1041598e..d3ca9a90ce 100644 --- a/Documentation/git-archive.txt +++ b/Documentation/git-archive.txt @@ -33,9 +33,12 @@ OPTIONS Format of the resulting archive: 'tar', 'zip'... The default is 'tar'. ---list:: +--list, -l:: Show all available formats. +--verbose, -v:: + Report progress to stderr. + --prefix=<prefix>/:: Prepend <prefix>/ to each filename in the archive. diff --git a/Documentation/git-cherry-pick.txt b/Documentation/git-cherry-pick.txt index 3149d08da8..68bba98260 100644 --- a/Documentation/git-cherry-pick.txt +++ b/Documentation/git-cherry-pick.txt @@ -38,7 +38,7 @@ OPTIONS development branch), adding this information can be useful. --r|--replay:: +-r:: It used to be that the command defaulted to do `-x` described above, and `-r` was to disable it. Now the default is not to do `-x` so this option is a no-op. diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt index c759efb7fc..280ef2058c 100644 --- a/Documentation/git-config.txt +++ b/Documentation/git-config.txt @@ -9,16 +9,16 @@ git-config - Get and set repository or global options SYNOPSIS -------- [verse] -'git-config' [--global] [type] name [value [value_regex]] -'git-config' [--global] [type] --add name value -'git-config' [--global] [type] --replace-all name [value [value_regex]] -'git-config' [--global] [type] --get name [value_regex] -'git-config' [--global] [type] --get-all name [value_regex] -'git-config' [--global] [type] --unset name [value_regex] -'git-config' [--global] [type] --unset-all name [value_regex] -'git-config' [--global] [type] --rename-section old_name new_name -'git-config' [--global] [type] --remove-section name -'git-config' [--global] -l | --list +'git-config' [--system | --global] [type] name [value [value_regex]] +'git-config' [--system | --global] [type] --add name value +'git-config' [--system | --global] [type] --replace-all name [value [value_regex]] +'git-config' [--system | --global] [type] --get name [value_regex] +'git-config' [--system | --global] [type] --get-all name [value_regex] +'git-config' [--system | --global] [type] --unset name [value_regex] +'git-config' [--system | --global] [type] --unset-all name [value_regex] +'git-config' [--system | --global] [type] --rename-section old_name new_name +'git-config' [--system | --global] [type] --remove-section name +'git-config' [--system | --global] -l | --list DESCRIPTION ----------- @@ -76,6 +76,10 @@ OPTIONS --global:: Use global ~/.gitconfig file rather than the repository .git/config. +--system:: + Use system-wide $(prefix)/etc/gitconfig rather than the repository + .git/config. + --remove-section:: Remove the given section from the configuration file. diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt index 595940524e..7bde73b1b8 100644 --- a/Documentation/git-tar-tree.txt +++ b/Documentation/git-tar-tree.txt @@ -13,7 +13,7 @@ SYNOPSIS DESCRIPTION ----------- THIS COMMAND IS DEPRECATED. Use `git-archive` with `--format=tar` -option instead. +option instead (and move the <base> argument to `--prefix=base/`). Creates a tar archive containing the tree structure for the named tree. When <base> is specified it is added as a leading path to the files in the diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt index 129c5c5f5b..e978562d6e 100644 --- a/Documentation/tutorial.txt +++ b/Documentation/tutorial.txt @@ -111,7 +111,7 @@ make it real. Note: don't forget to 'add' a file again if you modified it after the first 'add' and before 'commit'. Otherwise only the previous added state of that file will be committed. This is because git tracks -content, so what you're really 'add'ing to the commit is the *content* +content, so what you're really 'adding' to the commit is the *content* of the file in the state it is in when you 'add' it. 2) By using 'git commit -a' directly diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 2325660ff4..41ee8b4ea2 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.1.1.GIT +DEF_VER=v1.5.1.2.GIT LF=' ' diff --git a/builtin-config.c b/builtin-config.c index dfa403b94b..b2515f7e65 100644 --- a/builtin-config.c +++ b/builtin-config.c @@ -2,7 +2,7 @@ #include "cache.h" static const char git_config_set_usage[] = -"git-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list"; +"git-config [ --global | --system ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list"; static char *key; static regex_t *key_regexp; diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c index e9d6764550..be341c159f 100644 --- a/builtin-fetch--tool.c +++ b/builtin-fetch--tool.c @@ -436,10 +436,87 @@ static int expand_refs_wildcard(const char *ls_remote_result, int numrefs, return 0; } +static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_result) +{ + int err = 0; + int lrr_count = lrr_count, i, pass; + const char *cp; + struct lrr { + const char *line; + const char *name; + int namelen; + int shown; + } *lrr_list = lrr_list; + + for (pass = 0; pass < 2; pass++) { + /* pass 0 counts and allocates, pass 1 fills... */ + cp = ls_remote_result; + i = 0; + while (1) { + const char *np; + while (*cp && isspace(*cp)) + cp++; + if (!*cp) + break; + np = strchr(cp, '\n'); + if (!np) + np = cp + strlen(cp); + if (pass) { + lrr_list[i].line = cp; + lrr_list[i].name = cp + 41; + lrr_list[i].namelen = np - (cp + 41); + } + i++; + cp = np; + } + if (!pass) { + lrr_count = i; + lrr_list = xcalloc(lrr_count, sizeof(*lrr_list)); + } + } + + while (1) { + const char *next; + int rreflen; + int i; + + while (*rref && isspace(*rref)) + rref++; + if (!*rref) + break; + next = strchr(rref, '\n'); + if (!next) + next = rref + strlen(rref); + rreflen = next - rref; + + for (i = 0; i < lrr_count; i++) { + struct lrr *lrr = &(lrr_list[i]); + + if (rreflen == lrr->namelen && + !memcmp(lrr->name, rref, rreflen)) { + if (!lrr->shown) + printf("%.*s\n", + sha1_only ? 40 : lrr->namelen + 41, + lrr->line); + lrr->shown = 1; + break; + } + } + if (lrr_count <= i) { + error("pick-rref: %.*s not found", rreflen, rref); + err = 1; + } + rref = next; + } + free(lrr_list); + return err; +} + int cmd_fetch__tool(int argc, const char **argv, const char *prefix) { int verbose = 0; int force = 0; + int sopt = 0; while (1 < argc) { const char *arg = argv[1]; @@ -447,6 +524,8 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix) verbose = 1; else if (!strcmp("-f", arg)) force = 1; + else if (!strcmp("-s", arg)) + sopt = 1; else break; argc--; @@ -491,6 +570,11 @@ int cmd_fetch__tool(int argc, const char **argv, const char *prefix) reflist = get_stdin(); return parse_reflist(reflist); } + if (!strcmp("pick-rref", argv[1])) { + if (argc != 4) + return error("pick-rref takes 2 args"); + return pick_rref(sopt, argv[2], argv[3]); + } if (!strcmp("expand-refs-wildcard", argv[1])) { const char *reflist; if (argc < 4) diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 09774f9559..c0329dcecd 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -113,6 +113,10 @@ static void show_object(struct object_array_entry *p) * confuse downstream git-pack-objects very badly. */ const char *ep = strchr(p->name, '\n'); + + if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1)) + die("missing blob object '%s'", sha1_to_hex(p->item->sha1)); + if (ep) { printf("%s %.*s\n", sha1_to_hex(p->item->sha1), (int) (ep - p->name), diff --git a/builtin-update-index.c b/builtin-update-index.c index b3d4acee6d..e5541df284 100644 --- a/builtin-update-index.c +++ b/builtin-update-index.c @@ -551,7 +551,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix) if (i+3 >= argc) die("git-update-index: --cacheinfo <mode> <sha1> <path>"); - if ((strtoul_ui(argv[i+1], 8, &mode) != 1) || + if (strtoul_ui(argv[i+1], 8, &mode) || get_sha1_hex(argv[i+2], sha1) || add_cacheinfo(mode, sha1, argv[i+3], 0)) die("git-update-index: --cacheinfo" diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 2f9995ea39..f60017948f 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -345,9 +345,15 @@ and returns the process output as a string." (let ((str (git-call-process-env-string nil "symbolic-ref" ref))) (and str (car (split-string str "\n"))))) -(defun git-update-ref (ref val &optional oldval) +(defun git-update-ref (ref newval &optional oldval reason) "Update a reference by calling git-update-ref." - (apply #'git-call-process-env nil nil "update-ref" ref val (if oldval (list oldval)))) + (let ((args (and oldval (list oldval)))) + (push newval args) + (push ref args) + (when reason + (push reason args) + (push "-m" args)) + (eq 0 (apply #'git-call-process-env nil nil "update-ref" args)))) (defun git-read-tree (tree &optional index-file) "Read a tree into the index file." @@ -364,8 +370,10 @@ and returns the process output as a string." "Call git-commit-tree with buffer as input and return the resulting commit SHA1." (let ((author-name (git-get-committer-name)) (author-email (git-get-committer-email)) + (subject "commit (initial): ") author-date log-start log-end args coding-system-for-write) (when head + (setq subject "commit: ") (push "-p" args) (push head args)) (with-current-buffer buffer @@ -384,22 +392,29 @@ and returns the process output as a string." (goto-char (point-min)) (while (re-search-forward "^Parent: +\\([0-9a-f]+\\)" nil t) (unless (string-equal head (match-string 1)) + (setq subject "commit (merge): ") (push "-p" args) (push (match-string 1) args)))) (setq log-start (point-min))) (setq log-end (point-max)) + (goto-char log-start) + (when (re-search-forward ".*$" nil t) + (setq subject (concat subject (match-string 0)))) (setq coding-system-for-write buffer-file-coding-system)) - (git-get-string-sha1 - (with-output-to-string - (with-current-buffer standard-output - (let ((env `(("GIT_AUTHOR_NAME" . ,author-name) - ("GIT_AUTHOR_EMAIL" . ,author-email) - ("GIT_COMMITTER_NAME" . ,(git-get-committer-name)) - ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email))))) - (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env)) - (apply #'git-run-command-region - buffer log-start log-end env - "commit-tree" tree (nreverse args)))))))) + (let ((commit + (git-get-string-sha1 + (with-output-to-string + (with-current-buffer standard-output + (let ((env `(("GIT_AUTHOR_NAME" . ,author-name) + ("GIT_AUTHOR_EMAIL" . ,author-email) + ("GIT_COMMITTER_NAME" . ,(git-get-committer-name)) + ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email))))) + (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env)) + (apply #'git-run-command-region + buffer log-start log-end env + "commit-tree" tree (nreverse args)))))))) + (and (git-update-ref "HEAD" commit head subject) + commit)))) (defun git-empty-db-p () "Check if the git db is empty (no commit done yet)." @@ -662,7 +677,6 @@ and returns the process output as a string." (if (or (not (string-equal tree head-tree)) (yes-or-no-p "The tree was not modified, do you really want to perform an empty commit? ")) (let ((commit (git-commit-tree buffer tree head))) - (git-update-ref "HEAD" commit head) (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil)) (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 521b2fcd32..2d80e2bad2 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -10,7 +10,8 @@ GUI browser for git repository This program is based on bzrk by Scott James Remnant <scott@ubuntu.com> """ __copyright__ = "Copyright (C) 2006 Hewlett-Packard Development Company, L.P." -__author__ = "Aneesh Kumar K.V <aneesh.kumar@hp.com>" +__copyright__ = "Copyright (C) 2007 Aneesh Kumar K.V <aneesh.kumar@gmail.com" +__author__ = "Aneesh Kumar K.V <aneesh.kumar@gmail.com>" import sys @@ -24,6 +25,7 @@ import gobject import cairo import math import string +import fcntl try: import gtksourceview @@ -337,6 +339,186 @@ class Commit: fp.close() return diff +class AnnotateWindow: + """Annotate window. + This object represents and manages a single window containing the + annotate information of the file + """ + + def __init__(self): + self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) + self.window.set_border_width(0) + self.window.set_title("Git repository browser annotation window") + + # Use two thirds of the screen by default + screen = self.window.get_screen() + monitor = screen.get_monitor_geometry(0) + width = int(monitor.width * 0.66) + height = int(monitor.height * 0.66) + self.window.set_default_size(width, height) + + def add_file_data(self, filename, commit_sha1, line_num): + fp = os.popen("git cat-file blob " + commit_sha1 +":"+filename) + i = 1; + for line in fp.readlines(): + line = string.rstrip(line) + self.model.append(None, ["HEAD", filename, line, i]) + i = i+1 + fp.close() + + # now set the cursor position + self.treeview.set_cursor(line_num-1) + self.treeview.grab_focus() + + def _treeview_cursor_cb(self, *args): + """Callback for when the treeview cursor changes.""" + (path, col) = self.treeview.get_cursor() + commit_sha1 = self.model[path][0] + commit_msg = "" + fp = os.popen("git cat-file commit " + commit_sha1) + for line in fp.readlines(): + commit_msg = commit_msg + line + fp.close() + + self.commit_buffer.set_text(commit_msg) + + def _treeview_row_activated(self, *args): + """Callback for when the treeview row gets selected.""" + (path, col) = self.treeview.get_cursor() + commit_sha1 = self.model[path][0] + filename = self.model[path][1] + line_num = self.model[path][3] + + window = AnnotateWindow(); + fp = os.popen("git rev-parse "+ commit_sha1 + "~1") + commit_sha1 = string.strip(fp.readline()) + fp.close() + window.annotate(filename, commit_sha1, line_num) + + def data_ready(self, source, condition): + while (1): + try : + buffer = source.read(8192) + except: + # resource temporary not available + return True + + if (len(buffer) == 0): + gobject.source_remove(self.io_watch_tag) + source.close() + return False + + for buff in buffer.split("\n"): + annotate_line = re.compile('^([0-9a-f]{40}) (.+) (.+) (.+)$') + m = annotate_line.match(buff) + if not m: + annotate_line = re.compile('^(filename) (.+)$') + m = annotate_line.match(buff) + if not m: + continue + filename = m.group(2) + else: + self.commit_sha1 = m.group(1) + self.source_line = int(m.group(2)) + self.result_line = int(m.group(3)) + self.count = int(m.group(4)) + #set the details only when we have the file name + continue + + while (self.count > 0): + # set at result_line + count-1 the sha1 as commit_sha1 + self.count = self.count - 1 + iter = self.model.iter_nth_child(None, self.result_line + self.count-1) + self.model.set(iter, 0, self.commit_sha1, 1, filename, 3, self.source_line) + + + def annotate(self, filename, commit_sha1, line_num): + # verify the commit_sha1 specified has this filename + + fp = os.popen("git ls-tree "+ commit_sha1 + " -- " + filename) + line = string.strip(fp.readline()) + if line == '': + # pop up the message the file is not there as a part of the commit + fp.close() + dialog = gtk.MessageDialog(parent=None, flags=0, + type=gtk.MESSAGE_WARNING, buttons=gtk.BUTTONS_CLOSE, + message_format=None) + dialog.set_markup("The file %s is not present in the parent commit %s" % (filename, commit_sha1)) + dialog.run() + dialog.destroy() + return + + fp.close() + + vpan = gtk.VPaned(); + self.window.add(vpan); + vpan.show() + + scrollwin = gtk.ScrolledWindow() + scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrollwin.set_shadow_type(gtk.SHADOW_IN) + vpan.pack1(scrollwin, True, True); + scrollwin.show() + + self.model = gtk.TreeStore(str, str, str, int) + self.treeview = gtk.TreeView(self.model) + self.treeview.set_rules_hint(True) + self.treeview.set_search_column(0) + self.treeview.connect("cursor-changed", self._treeview_cursor_cb) + self.treeview.connect("row-activated", self._treeview_row_activated) + scrollwin.add(self.treeview) + self.treeview.show() + + cell = gtk.CellRendererText() + cell.set_property("width-chars", 10) + cell.set_property("ellipsize", pango.ELLIPSIZE_END) + column = gtk.TreeViewColumn("Commit") + column.set_resizable(True) + column.pack_start(cell, expand=True) + column.add_attribute(cell, "text", 0) + self.treeview.append_column(column) + + cell = gtk.CellRendererText() + cell.set_property("width-chars", 20) + cell.set_property("ellipsize", pango.ELLIPSIZE_END) + column = gtk.TreeViewColumn("File Name") + column.set_resizable(True) + column.pack_start(cell, expand=True) + column.add_attribute(cell, "text", 1) + self.treeview.append_column(column) + + cell = gtk.CellRendererText() + cell.set_property("width-chars", 20) + cell.set_property("ellipsize", pango.ELLIPSIZE_END) + column = gtk.TreeViewColumn("Data") + column.set_resizable(True) + column.pack_start(cell, expand=True) + column.add_attribute(cell, "text", 2) + self.treeview.append_column(column) + + # The commit message window + scrollwin = gtk.ScrolledWindow() + scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrollwin.set_shadow_type(gtk.SHADOW_IN) + vpan.pack2(scrollwin, True, True); + scrollwin.show() + + commit_text = gtk.TextView() + self.commit_buffer = gtk.TextBuffer() + commit_text.set_buffer(self.commit_buffer) + scrollwin.add(commit_text) + commit_text.show() + + self.window.show() + + self.add_file_data(filename, commit_sha1, line_num) + + fp = os.popen("git blame --incremental -- " + filename + " " + commit_sha1) + flags = fcntl.fcntl(fp.fileno(), fcntl.F_GETFL) + fcntl.fcntl(fp.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK) + self.io_watch_tag = gobject.io_add_watch(fp, gobject.IO_IN, self.data_ready) + + class DiffWindow: """Diff window. This object represents and manages a single window containing the @@ -355,6 +537,7 @@ class DiffWindow: height = int(monitor.height * 0.66) self.window.set_default_size(width, height) + self.construct() def construct(self): @@ -371,10 +554,12 @@ class DiffWindow: vbox.pack_start(menu_bar, expand=False, fill=True) menu_bar.show() + hpan = gtk.HPaned() + scrollwin = gtk.ScrolledWindow() scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) scrollwin.set_shadow_type(gtk.SHADOW_IN) - vbox.pack_start(scrollwin, expand=True, fill=True) + hpan.pack1(scrollwin, True, True) scrollwin.show() if have_gtksourceview: @@ -388,11 +573,77 @@ class DiffWindow: self.buffer = gtk.TextBuffer() sourceview = gtk.TextView(self.buffer) + sourceview.set_editable(False) sourceview.modify_font(pango.FontDescription("Monospace")) scrollwin.add(sourceview) sourceview.show() + # The file hierarchy: a scrollable treeview + scrollwin = gtk.ScrolledWindow() + scrollwin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrollwin.set_shadow_type(gtk.SHADOW_IN) + scrollwin.set_size_request(20, -1) + hpan.pack2(scrollwin, True, True) + scrollwin.show() + + self.model = gtk.TreeStore(str, str, str) + self.treeview = gtk.TreeView(self.model) + self.treeview.set_search_column(1) + self.treeview.connect("cursor-changed", self._treeview_clicked) + scrollwin.add(self.treeview) + self.treeview.show() + + cell = gtk.CellRendererText() + cell.set_property("width-chars", 20) + column = gtk.TreeViewColumn("Select to annotate") + column.pack_start(cell, expand=True) + column.add_attribute(cell, "text", 0) + self.treeview.append_column(column) + + vbox.pack_start(hpan, expand=True, fill=True) + hpan.show() + + def _treeview_clicked(self, *args): + """Callback for when the treeview cursor changes.""" + (path, col) = self.treeview.get_cursor() + specific_file = self.model[path][1] + commit_sha1 = self.model[path][2] + if specific_file == None : + return + elif specific_file == "" : + specific_file = None + + window = AnnotateWindow(); + window.annotate(specific_file, commit_sha1, 1) + + + def commit_files(self, commit_sha1, parent_sha1): + self.model.clear() + add = self.model.append(None, [ "Added", None, None]) + dele = self.model.append(None, [ "Deleted", None, None]) + mod = self.model.append(None, [ "Modified", None, None]) + diff_tree = re.compile('^(:.{6}) (.{6}) (.{40}) (.{40}) (A|D|M)\s(.+)$') + fp = os.popen("git diff-tree -r --no-commit-id " + parent_sha1 + " " + commit_sha1) + while 1: + line = string.strip(fp.readline()) + if line == '': + break + m = diff_tree.match(line) + if not m: + continue + + attr = m.group(5) + filename = m.group(6) + if attr == "A": + self.model.append(add, [filename, filename, commit_sha1]) + elif attr == "D": + self.model.append(dele, [filename, filename, commit_sha1]) + elif attr == "M": + self.model.append(mod, [filename, filename, commit_sha1]) + fp.close() + + self.treeview.expand_all() def set_diff(self, commit_sha1, parent_sha1, encoding): """Set the differences showed by this window. @@ -406,6 +657,7 @@ class DiffWindow: fp = os.popen("git diff-tree -p " + parent_sha1 + " " + commit_sha1) self.buffer.set_text(unicode(fp.read(), encoding).encode('utf-8')) fp.close() + self.commit_files(commit_sha1, parent_sha1) self.window.show() def save_menu_response(self, widget, string): @@ -425,7 +677,7 @@ class DiffWindow: class GitView: """ This is the main class """ - version = "0.8" + version = "0.9" def __init__(self, with_diff=0): self.with_diff = with_diff @@ -590,7 +842,7 @@ class GitView: dialog = gtk.AboutDialog() dialog.set_name("Gitview") dialog.set_version(GitView.version) - dialog.set_authors(["Aneesh Kumar K.V <aneesh.kumar@hp.com>"]) + dialog.set_authors(["Aneesh Kumar K.V <aneesh.kumar@gmail.com>"]) dialog.set_website("http://www.kernel.org/pub/software/scm/git/") dialog.set_copyright("Use and distribute under the terms of the GNU General Public License") dialog.set_wrap_license(True) diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid new file mode 100644 index 0000000000..5ee1835c80 --- /dev/null +++ b/contrib/hooks/update-paranoid @@ -0,0 +1,284 @@ +#!/usr/bin/perl + +use strict; +use File::Spec; + +$ENV{PATH} = '/opt/git/bin'; +my $acl_git = '/vcs/acls.git'; +my $acl_branch = 'refs/heads/master'; +my $debug = 0; + +=doc +Invoked as: update refname old-sha1 new-sha1 + +This script is run by git-receive-pack once for each ref that the +client is trying to modify. If we exit with a non-zero exit value +then the update for that particular ref is denied, but updates for +other refs in the same run of receive-pack may still be allowed. + +We are run after the objects have been uploaded, but before the +ref is actually modified. We take advantage of that fact when we +look for "new" commits and tags (the new objects won't show up in +`rev-list --all`). + +This script loads and parses the content of the config file +"users/$this_user.acl" from the $acl_branch commit of $acl_git ODB. +The acl file is a git-config style file, but uses a slightly more +restricted syntax as the Perl parser contained within this script +is not nearly as permissive as git-config. + +Example: + + [user] + committer = John Doe <john.doe@example.com> + committer = John R. Doe <john.doe@example.com> + + [repository "acls"] + allow = heads/master + allow = CDUR for heads/jd/ + allow = C for ^tags/v\\d+$ + +For all new commit or tag objects the committer (or tagger) line +within the object must exactly match one of the user.committer +values listed in the acl file ("HEAD:users/$this_user.acl"). + +For a branch to be modified an allow line within the matching +repository section must be matched for both the refname and the +opcode. + +Repository sections are matched on the basename of the repository +(after removing the .git suffix). + +The opcode abbrevations are: + + C: create new ref + D: delete existing ref + U: fast-forward existing ref (no commit loss) + R: rewind/rebase existing ref (commit loss) + +if no opcodes are listed before the "for" keyword then "U" (for +fast-forward update only) is assumed as this is the most common +usage. + +Refnames are matched by always assuming a prefix of "refs/". +This hook forbids pushing or deleting anything not under "refs/". + +Refnames that start with ^ are Perl regular expressions, and the ^ +is kept as part of the regexp. \\ is needed to get just one \, so +\\d expands to \d in Perl. The 3rd allow line above is an example. + +Refnames that don't start with ^ but that end with / are prefix +matches (2nd allow line above); all other refnames are strict +equality matches (1st allow line). + +Anything pushed to "heads/" (ok, really "refs/heads/") must be +a commit. Tags are not permitted here. + +Anything pushed to "tags/" (err, really "refs/tags/") must be an +annotated tag. Commits, blobs, trees, etc. are not permitted here. +Annotated tag signatures aren't checked, nor are they required. + +The special subrepository of 'info/new-commit-check' can +be created and used to allow users to push new commits and +tags from another local repository to this one, even if they +aren't the committer/tagger of those objects. In a nut shell +the info/new-commit-check directory is a Git repository whose +objects/info/alternates file lists this repository and all other +possible sources, and whose refs subdirectory contains symlinks +to this repository's refs subdirectory, and to all other possible +sources refs subdirectories. Yes, this means that you cannot +use packed-refs in those repositories as they won't be resolved +correctly. + +=cut + +my $git_dir = $ENV{GIT_DIR}; +my $new_commit_check = "$git_dir/info/new-commit-check"; +my $ref = $ARGV[0]; +my $old = $ARGV[1]; +my $new = $ARGV[2]; +my $new_type; +my ($this_user) = getpwuid $<; # REAL_USER_ID +my $repository_name; +my %user_committer; +my @allow_rules; + +sub deny ($) { + print STDERR "-Deny- $_[0]\n" if $debug; + print STDERR "\ndenied: $_[0]\n\n"; + exit 1; +} + +sub grant ($) { + print STDERR "-Grant- $_[0]\n" if $debug; + exit 0; +} + +sub info ($) { + print STDERR "-Info- $_[0]\n" if $debug; +} + +sub parse_config ($$) { + my ($data, $fn) = @_; + info "Loading $fn"; + open(I,'-|','git',"--git-dir=$acl_git",'cat-file','blob',$fn); + my $section = ''; + while (<I>) { + chomp; + if (/^\s*$/ || /^\s*#/) { + } elsif (/^\[([a-z]+)\]$/i) { + $section = $1; + } elsif (/^\[([a-z]+)\s+"(.*)"\]$/i) { + $section = "$1.$2"; + } elsif (/^\s*([a-z][a-z0-9]+)\s*=\s*(.*?)\s*$/i) { + push @{$data->{"$section.$1"}}, $2; + } else { + deny "bad config file line $. in $fn"; + } + } + close I; +} + +sub all_new_committers () { + local $ENV{GIT_DIR} = $git_dir; + $ENV{GIT_DIR} = $new_commit_check if -d $new_commit_check; + + info "Getting committers of new commits."; + my %used; + open(T,'-|','git','rev-list','--pretty=raw',$new,'--not','--all'); + while (<T>) { + next unless s/^committer //; + chop; + s/>.*$/>/; + info "Found $_." unless $used{$_}++; + } + close T; + info "No new commits." unless %used; + keys %used; +} + +sub all_new_taggers () { + my %exists; + open(T,'-|','git','for-each-ref','--format=%(objectname)','refs/tags'); + while (<T>) { + chop; + $exists{$_} = 1; + } + close T; + + info "Getting taggers of new tags."; + my %used; + my $obj = $new; + my $obj_type = $new_type; + while ($obj_type eq 'tag') { + last if $exists{$obj}; + $obj_type = ''; + open(T,'-|','git','cat-file','tag',$obj); + while (<T>) { + chop; + if (/^object ([a-z0-9]{40})$/) { + $obj = $1; + } elsif (/^type (.+)$/) { + $obj_type = $1; + } elsif (s/^tagger //) { + s/>.*$/>/; + info "Found $_." unless $used{$_}++; + last; + } + } + close T; + } + info "No new tags." unless %used; + keys %used; +} + +sub check_committers (@) { + my @bad; + foreach (@_) { push @bad, $_ unless $user_committer{$_}; } + if (@bad) { + print STDERR "\n"; + print STDERR "You are not $_.\n" foreach (sort @bad); + deny "You cannot push changes not committed by you."; + } +} + +sub git_value (@) { + open(T,'-|','git',@_); local $_ = <T>; chop; close T; + $_; +} + +deny "No GIT_DIR inherited from caller" unless $git_dir; +deny "Need a ref name" unless $ref; +deny "Refusing funny ref $ref" unless $ref =~ s,^refs/,,; +deny "Bad old value $old" unless $old =~ /^[a-z0-9]{40}$/; +deny "Bad new value $new" unless $new =~ /^[a-z0-9]{40}$/; +deny "Cannot determine who you are." unless $this_user; + +$repository_name = File::Spec->rel2abs($git_dir); +$repository_name =~ m,/([^/]+)(?:\.git|/\.git)$,; +$repository_name = $1; +info "Updating in '$repository_name'."; + +my $op; +if ($old =~ /^0{40}$/) { $op = 'C'; } +elsif ($new =~ /^0{40}$/) { $op = 'D'; } +else { $op = 'R'; } + +# This is really an update (fast-forward) if the +# merge base of $old and $new is $old. +# +$op = 'U' if ($op eq 'R' + && $ref =~ m,^heads/, + && $old eq git_value('merge-base',$old,$new)); + +# Load the user's ACL file. +{ + my %data = ('user.committer' => []); + parse_config(\%data, "$acl_branch:users/$this_user.acl"); + %user_committer = map {$_ => $_} @{$data{'user.committer'}}; + my $rules = $data{"repository.$repository_name.allow"} || []; + foreach (@$rules) { + if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { + my $ops = $1; + my $ref = $2; + $ops =~ s/ //g; + $ref =~ s/\\\\/\\/g; + push @allow_rules, [$ops, $ref]; + } elsif (/^for\s+([^\s]+)$/) { + # Mentioned, but nothing granted? + } elsif (/^[^\s]+$/) { + s/\\\\/\\/g; + push @allow_rules, ['U', $_]; + } + } +} + +if ($op ne 'D') { + $new_type = git_value('cat-file','-t',$new); + + if ($ref =~ m,^heads/,) { + deny "$ref must be a commit." unless $new_type eq 'commit'; + } elsif ($ref =~ m,^tags/,) { + deny "$ref must be an annotated tag." unless $new_type eq 'tag'; + } + + check_committers (all_new_committers); + check_committers (all_new_taggers) if $new_type eq 'tag'; +} + +info "$this_user wants $op for $ref"; +foreach my $acl_entry (@allow_rules) { + my ($acl_ops, $acl_n) = @$acl_entry; + next unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen. + next unless $acl_n; + next unless $op =~ /^[$acl_ops]$/; + + grant "Allowed by: $acl_ops for $acl_n" + if ( + ($acl_n eq $ref) + || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n) + || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:) + ); +} +close A; +deny "You are not permitted to $op $ref"; diff --git a/convert-objects.c b/convert-objects.c index cf03bcfe5a..cefbcebdca 100644 --- a/convert-objects.c +++ b/convert-objects.c @@ -88,7 +88,7 @@ static int write_subdirectory(void *buffer, unsigned long size, const char *base unsigned int mode; char *slash, *origpath; - if (!path || strtoul_ui(buffer, 8, &mode) != 1) + if (!path || strtoul_ui(buffer, 8, &mode)) die("bad tree conversion"); mode = convert_mode(mode); path++; diff --git a/decorate.c b/decorate.c index 396b41311a..23f6b0040f 100644 --- a/decorate.c +++ b/decorate.c @@ -24,7 +24,6 @@ static void *insert_decoration(struct decoration *n, struct object *base, void * hash[j].decoration = decoration; return old; } - j++; if (++j >= size) j = 0; } diff --git a/git-clone.sh b/git-clone.sh index 513b574d13..cad5c0c088 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -60,7 +60,7 @@ Perhaps git-update-server-info needs to be run there?" else tname=$name fi - git-http-fetch $v -a -w "$tname" "$name" "$1" || exit 1 + git-http-fetch $v -a -w "$tname" "$sha1" "$1" || exit 1 done <"$clone_tmp/refs" rm -fr "$clone_tmp" http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD" || diff --git a/git-fetch.sh b/git-fetch.sh index b04bd553f8..832b20cce6 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -177,9 +177,33 @@ fetch_all_at_once () { git-bundle unbundle "$remote" $rref || echo failed "$remote" else - git-fetch-pack --thin $exec $keep $shallow_depth \ - $quiet $no_progress "$remote" $rref || - echo failed "$remote" + if test -d "$remote" && + + # The remote might be our alternate. With + # this optimization we will bypass fetch-pack + # altogether, which means we cannot be doing + # the shallow stuff at all. + test ! -f "$GIT_DIR/shallow" && + test -z "$shallow_depth" && + + # See if all of what we are going to fetch are + # connected to our repository's tips, in which + # case we do not have to do any fetch. + theirs=$(git-fetch--tool -s pick-rref \ + "$rref" "$ls_remote_result") && + + # This will barf when $theirs reach an object that + # we do not have in our repository. Otherwise, + # we already have everything the fetch would bring in. + git-rev-list --objects $theirs --not --all \ + >/dev/null 2>/dev/null + then + git-fetch--tool pick-rref "$rref" "$ls_remote_result" + else + git-fetch-pack --thin $exec $keep $shallow_depth \ + $quiet $no_progress "$remote" $rref || + echo failed "$remote" + fi fi ) | ( @@ -239,16 +263,8 @@ fetch_per_ref () { fi # Find $remote_name from ls-remote output. - head=$( - IFS=' ' - echo "$ls_remote_result" | - while read sha1 name - do - test "z$name" = "z$remote_name" || continue - echo "$sha1" - break - done - ) + head=$(git-fetch--tool -s pick-rref \ + "$remote_name" "$ls_remote_result") expr "z$head" : "z$_x40\$" >/dev/null || die "No such ref $remote_name at $remote" echo >&2 "Fetching $remote_name from $remote using $proto" diff --git a/merge-recursive.c b/merge-recursive.c index 595b0226ac..31e66e5ca3 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -95,11 +95,6 @@ static struct path_list current_directory_set = {NULL, 0, 0, 1}; static int call_depth = 0; static int verbosity = 2; static int buffer_output = 1; -static int do_progress = 1; -static unsigned last_percent; -static unsigned merged_cnt; -static unsigned total_cnt; -static volatile sig_atomic_t progress_update; static struct output_buffer *output_list, *output_end; static int show (int v) @@ -174,39 +169,6 @@ static void output_commit_title(struct commit *commit) } } -static void progress_interval(int signum) -{ - progress_update = 1; -} - -static void setup_progress_signal(void) -{ - struct sigaction sa; - struct itimerval v; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = progress_interval; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - sigaction(SIGALRM, &sa, NULL); - - v.it_interval.tv_sec = 1; - v.it_interval.tv_usec = 0; - v.it_value = v.it_interval; - setitimer(ITIMER_REAL, &v, NULL); -} - -static void display_progress() -{ - unsigned percent = total_cnt ? merged_cnt * 100 / total_cnt : 0; - if (progress_update || percent != last_percent) { - fprintf(stderr, "%4u%% (%u/%u) done\r", - percent, merged_cnt, total_cnt); - progress_update = 0; - last_percent = percent; - } -} - static struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh) { @@ -377,14 +339,11 @@ static struct path_list *get_unmerged(void) int i; unmerged->strdup_paths = 1; - total_cnt += active_nr; - for (i = 0; i < active_nr; i++, merged_cnt++) { + for (i = 0; i < active_nr; i++) { struct path_list_item *item; struct stage_data *e; struct cache_entry *ce = active_cache[i]; - if (do_progress) - display_progress(); if (!ce_stage(ce)) continue; @@ -574,6 +533,31 @@ static void flush_buffer(int fd, const char *buf, unsigned long size) } } +static int make_room_for_path(const char *path) +{ + int status; + const char *msg = "failed to create path '%s'%s"; + + status = mkdir_p(path, 0777); + if (status) { + if (status == -3) { + /* something else exists */ + error(msg, path, ": perhaps a D/F conflict?"); + return -1; + } + die(msg, path, ""); + } + + /* Successful unlink is good.. */ + if (!unlink(path)) + return 0; + /* .. and so is no existing file */ + if (errno == ENOENT) + return 0; + /* .. but not some other error (who really cares what?) */ + return error(msg, path, ": perhaps a D/F conflict?"); +} + static void update_file_flags(const unsigned char *sha, unsigned mode, const char *path, @@ -594,33 +578,12 @@ static void update_file_flags(const unsigned char *sha, if (type != OBJ_BLOB) die("blob expected for %s '%s'", sha1_to_hex(sha), path); + if (make_room_for_path(path) < 0) { + update_wd = 0; + goto update_index; + } if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) { int fd; - int status; - const char *msg = "failed to create path '%s'%s"; - - status = mkdir_p(path, 0777); - if (status) { - if (status == -3) { - /* something else exists */ - error(msg, path, ": perhaps a D/F conflict?"); - update_wd = 0; - goto update_index; - } - die(msg, path, ""); - } - if (unlink(path)) { - if (errno == EISDIR) { - /* something else exists */ - error(msg, path, ": perhaps a D/F conflict?"); - update_wd = 0; - goto update_index; - } - if (errno != ENOENT) - die("failed to unlink %s " - "in preparation to update: %s", - path, strerror(errno)); - } if (mode & 0100) mode = 0777; else @@ -1214,15 +1177,12 @@ static int merge_trees(struct tree *head, re_merge = get_renames(merge, common, head, merge, entries); clean = process_renames(re_head, re_merge, branch1, branch2); - total_cnt += entries->nr; - for (i = 0; i < entries->nr; i++, merged_cnt++) { + for (i = 0; i < entries->nr; i++) { const char *path = entries->items[i].path; struct stage_data *e = entries->items[i].util; if (!e->processed && !process_entry(path, e, branch1, branch2)) clean = 0; - if (do_progress) - display_progress(); } path_list_clear(re_merge, 0); @@ -1330,15 +1290,6 @@ static int merge(struct commit *h1, commit_list_insert(h1, &(*result)->parents); commit_list_insert(h2, &(*result)->parents->next); } - if (!call_depth && do_progress) { - /* Make sure we end at 100% */ - if (!total_cnt) - total_cnt = 1; - merged_cnt = total_cnt; - progress_update = 1; - display_progress(); - fputc('\n', stderr); - } flush_output(); return clean; } @@ -1415,12 +1366,8 @@ int main(int argc, char *argv[]) } if (argc - i != 3) /* "--" "<head>" "<remote>" */ die("Not handling anything other than two heads merge."); - if (verbosity >= 5) { + if (verbosity >= 5) buffer_output = 0; - do_progress = 0; - } - else - do_progress = isatty(1); branch1 = argv[++i]; branch2 = argv[++i]; @@ -1431,8 +1378,6 @@ int main(int argc, char *argv[]) branch1 = better_branch_name(branch1); branch2 = better_branch_name(branch2); - if (do_progress) - setup_progress_signal(); if (show(3)) printf("Merging %s with %s\n", branch1, branch2); diff --git a/perl/Makefile.PL b/perl/Makefile.PL index 9b117fd0d7..437516142c 100644 --- a/perl/Makefile.PL +++ b/perl/Makefile.PL @@ -13,7 +13,7 @@ my %pm = ('Git.pm' => '$(INST_LIBDIR)/Git.pm'); # We come with our own bundled Error.pm. It's not in the set of default # Perl modules so install it if it's not available on the system yet. eval { require Error }; -if ($@) { +if ($@ || $Error::VERSION < 0.15009) { $pm{'private-Error.pm'} = '$(INST_LIBDIR)/Error.pm'; } diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh index aef92b9b92..86ee2b0bd3 100755 --- a/t/t3030-merge-recursive.sh +++ b/t/t3030-merge-recursive.sh @@ -9,12 +9,12 @@ test_expect_success 'setup 1' ' echo hello >a && o0=$(git hash-object a) && cp a b && - cp a A && + cp a c && mkdir d && cp a d/e && test_tick && - git add a b A d/e && + git add a b c d/e && git commit -m initial && c0=$(git rev-parse --verify HEAD) && git branch side && @@ -34,13 +34,13 @@ test_expect_success 'setup 1' ' c1=$(git rev-parse --verify HEAD) && ( git ls-tree -r HEAD ; git ls-files -s ) >actual && ( - echo "100644 blob $o0 A" echo "100644 blob $o1 a" echo "100644 blob $o0 b" + echo "100644 blob $o0 c" echo "100644 blob $o1 d/e" - echo "100644 $o0 0 A" echo "100644 $o1 0 a" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && git diff -u expected actual @@ -48,17 +48,17 @@ test_expect_success 'setup 1' ' test_expect_success 'setup 2' ' - rm -rf [Aabd] && + rm -rf [abcd] && git checkout side && ( git ls-tree -r HEAD ; git ls-files -s ) >actual && ( - echo "100644 blob $o0 A" echo "100644 blob $o0 a" echo "100644 blob $o0 b" + echo "100644 blob $o0 c" echo "100644 blob $o0 d/e" - echo "100644 $o0 0 A" echo "100644 $o0 0 a" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && git diff -u expected actual && @@ -73,13 +73,13 @@ test_expect_success 'setup 2' ' c2=$(git rev-parse --verify HEAD) && ( git ls-tree -r HEAD ; git ls-files -s ) >actual && ( - echo "100644 blob $o0 A" echo "100644 blob $o2 a" echo "100644 blob $o0 b" + echo "100644 blob $o0 c" echo "100644 blob $o0 d/e" - echo "100644 $o0 0 A" echo "100644 $o2 0 a" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && git diff -u expected actual @@ -87,17 +87,17 @@ test_expect_success 'setup 2' ' test_expect_success 'setup 3' ' - rm -rf [Aabd] && + rm -rf [abcd] && git checkout df-1 && ( git ls-tree -r HEAD ; git ls-files -s ) >actual && ( - echo "100644 blob $o0 A" echo "100644 blob $o0 a" echo "100644 blob $o0 b" + echo "100644 blob $o0 c" echo "100644 blob $o0 d/e" - echo "100644 $o0 0 A" echo "100644 $o0 0 a" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && git diff -u expected actual && @@ -110,13 +110,13 @@ test_expect_success 'setup 3' ' c3=$(git rev-parse --verify HEAD) && ( git ls-tree -r HEAD ; git ls-files -s ) >actual && ( - echo "100644 blob $o0 A" echo "100644 blob $o0 a" echo "100644 blob $o3 b/c" + echo "100644 blob $o0 c" echo "100644 blob $o0 d/e" - echo "100644 $o0 0 A" echo "100644 $o0 0 a" echo "100644 $o3 0 b/c" + echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && git diff -u expected actual @@ -124,17 +124,17 @@ test_expect_success 'setup 3' ' test_expect_success 'setup 4' ' - rm -rf [Aabd] && + rm -rf [abcd] && git checkout df-2 && ( git ls-tree -r HEAD ; git ls-files -s ) >actual && ( - echo "100644 blob $o0 A" echo "100644 blob $o0 a" echo "100644 blob $o0 b" + echo "100644 blob $o0 c" echo "100644 blob $o0 d/e" - echo "100644 $o0 0 A" echo "100644 $o0 0 a" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && git diff -u expected actual && @@ -147,13 +147,13 @@ test_expect_success 'setup 4' ' c4=$(git rev-parse --verify HEAD) && ( git ls-tree -r HEAD ; git ls-files -s ) >actual && ( - echo "100644 blob $o0 A" echo "100644 blob $o4 a/c" echo "100644 blob $o0 b" + echo "100644 blob $o0 c" echo "100644 blob $o0 d/e" - echo "100644 $o0 0 A" echo "100644 $o4 0 a/c" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && git diff -u expected actual @@ -161,17 +161,17 @@ test_expect_success 'setup 4' ' test_expect_success 'setup 5' ' - rm -rf [Aabd] && + rm -rf [abcd] && git checkout remove && ( git ls-tree -r HEAD ; git ls-files -s ) >actual && ( - echo "100644 blob $o0 A" echo "100644 blob $o0 a" echo "100644 blob $o0 b" + echo "100644 blob $o0 c" echo "100644 blob $o0 d/e" - echo "100644 $o0 0 A" echo "100644 $o0 0 a" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && git diff -u expected actual && @@ -188,11 +188,11 @@ test_expect_success 'setup 5' ' c5=$(git rev-parse --verify HEAD) && ( git ls-tree -r HEAD ; git ls-files -s ) >actual && ( - echo "100644 blob $o0 A" echo "100644 blob $o5 a" + echo "100644 blob $o0 c" echo "100644 blob $o0 d/e" - echo "100644 $o0 0 A" echo "100644 $o5 0 a" + echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && git diff -u expected actual @@ -201,17 +201,17 @@ test_expect_success 'setup 5' ' test_expect_success 'setup 6' ' - rm -rf [Aabd] && + rm -rf [abcd] && git checkout df-3 && ( git ls-tree -r HEAD ; git ls-files -s ) >actual && ( - echo "100644 blob $o0 A" echo "100644 blob $o0 a" echo "100644 blob $o0 b" + echo "100644 blob $o0 c" echo "100644 blob $o0 d/e" - echo "100644 $o0 0 A" echo "100644 $o0 0 a" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o0 0 d/e" ) >expected && git diff -u expected actual && @@ -224,13 +224,13 @@ test_expect_success 'setup 6' ' c6=$(git rev-parse --verify HEAD) && ( git ls-tree -r HEAD ; git ls-files -s ) >actual && ( - echo "100644 blob $o0 A" echo "100644 blob $o0 a" echo "100644 blob $o0 b" + echo "100644 blob $o0 c" echo "100644 blob $o6 d" - echo "100644 $o0 0 A" echo "100644 $o0 0 a" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o6 0 d" ) >expected && git diff -u expected actual @@ -238,7 +238,7 @@ test_expect_success 'setup 6' ' test_expect_success 'merge-recursive simple' ' - rm -fr [Aabd] && + rm -fr [abcd] && git checkout -f "$c2" && git-merge-recursive "$c0" -- "$c2" "$c1" @@ -258,11 +258,11 @@ test_expect_success 'merge-recursive result' ' git ls-files -s >actual && ( - echo "100644 $o0 0 A" echo "100644 $o0 1 a" echo "100644 $o2 2 a" echo "100644 $o1 3 a" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && git diff -u expected actual @@ -271,7 +271,7 @@ test_expect_success 'merge-recursive result' ' test_expect_success 'merge-recursive remove conflict' ' - rm -fr [Aabd] && + rm -fr [abcd] && git checkout -f "$c1" && git-merge-recursive "$c0" -- "$c1" "$c5" @@ -291,10 +291,10 @@ test_expect_success 'merge-recursive remove conflict' ' git ls-files -s >actual && ( - echo "100644 $o0 0 A" echo "100644 $o0 1 a" echo "100644 $o1 2 a" echo "100644 $o5 3 a" + echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && git diff -u expected actual @@ -302,7 +302,7 @@ test_expect_success 'merge-recursive remove conflict' ' ' test_expect_success 'merge-recursive d/f simple' ' - rm -fr [Aabd] && + rm -fr [abcd] && git reset --hard && git checkout -f "$c1" && @@ -313,9 +313,9 @@ test_expect_success 'merge-recursive result' ' git ls-files -s >actual && ( - echo "100644 $o0 0 A" echo "100644 $o1 0 a" echo "100644 $o3 0 b/c" + echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && git diff -u expected actual @@ -324,7 +324,7 @@ test_expect_success 'merge-recursive result' ' test_expect_success 'merge-recursive d/f conflict' ' - rm -fr [Aabd] && + rm -fr [abcd] && git reset --hard && git checkout -f "$c1" && @@ -345,11 +345,11 @@ test_expect_success 'merge-recursive d/f conflict result' ' git ls-files -s >actual && ( - echo "100644 $o0 0 A" echo "100644 $o0 1 a" echo "100644 $o1 2 a" echo "100644 $o4 0 a/c" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && git diff -u expected actual @@ -358,7 +358,7 @@ test_expect_success 'merge-recursive d/f conflict result' ' test_expect_success 'merge-recursive d/f conflict the other way' ' - rm -fr [Aabd] && + rm -fr [abcd] && git reset --hard && git checkout -f "$c4" && @@ -379,11 +379,11 @@ test_expect_success 'merge-recursive d/f conflict result the other way' ' git ls-files -s >actual && ( - echo "100644 $o0 0 A" echo "100644 $o0 1 a" echo "100644 $o1 3 a" echo "100644 $o4 0 a/c" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && git diff -u expected actual @@ -392,7 +392,7 @@ test_expect_success 'merge-recursive d/f conflict result the other way' ' test_expect_success 'merge-recursive d/f conflict' ' - rm -fr [Aabd] && + rm -fr [abcd] && git reset --hard && git checkout -f "$c1" && @@ -413,9 +413,9 @@ test_expect_success 'merge-recursive d/f conflict result' ' git ls-files -s >actual && ( - echo "100644 $o0 0 A" echo "100644 $o1 0 a" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o6 3 d" echo "100644 $o0 1 d/e" echo "100644 $o1 2 d/e" @@ -426,7 +426,7 @@ test_expect_success 'merge-recursive d/f conflict result' ' test_expect_success 'merge-recursive d/f conflict' ' - rm -fr [Aabd] && + rm -fr [abcd] && git reset --hard && git checkout -f "$c6" && @@ -447,9 +447,9 @@ test_expect_success 'merge-recursive d/f conflict result' ' git ls-files -s >actual && ( - echo "100644 $o0 0 A" echo "100644 $o1 0 a" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o6 2 d" echo "100644 $o0 1 d/e" echo "100644 $o1 3 d/e" @@ -471,13 +471,13 @@ test_expect_success 'reset and bind merge' ' git read-tree --prefix=M/ master && git ls-files -s >actual && ( - echo "100644 $o0 0 A" - echo "100644 $o0 0 M/A" echo "100644 $o1 0 M/a" echo "100644 $o0 0 M/b" + echo "100644 $o0 0 M/c" echo "100644 $o1 0 M/d/e" echo "100644 $o1 0 a" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && git diff -u expected actual && @@ -485,17 +485,17 @@ test_expect_success 'reset and bind merge' ' git read-tree --prefix=a1/ master && git ls-files -s >actual && ( - echo "100644 $o0 0 A" - echo "100644 $o0 0 M/A" echo "100644 $o1 0 M/a" echo "100644 $o0 0 M/b" + echo "100644 $o0 0 M/c" echo "100644 $o1 0 M/d/e" echo "100644 $o1 0 a" - echo "100644 $o0 0 a1/A" echo "100644 $o1 0 a1/a" echo "100644 $o0 0 a1/b" + echo "100644 $o0 0 a1/c" echo "100644 $o1 0 a1/d/e" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" ) >expected && git diff -u expected actual @@ -503,21 +503,21 @@ test_expect_success 'reset and bind merge' ' git read-tree --prefix=z/ master && git ls-files -s >actual && ( - echo "100644 $o0 0 A" - echo "100644 $o0 0 M/A" echo "100644 $o1 0 M/a" echo "100644 $o0 0 M/b" + echo "100644 $o0 0 M/c" echo "100644 $o1 0 M/d/e" echo "100644 $o1 0 a" - echo "100644 $o0 0 a1/A" echo "100644 $o1 0 a1/a" echo "100644 $o0 0 a1/b" + echo "100644 $o0 0 a1/c" echo "100644 $o1 0 a1/d/e" echo "100644 $o0 0 b" + echo "100644 $o0 0 c" echo "100644 $o1 0 d/e" - echo "100644 $o0 0 z/A" echo "100644 $o1 0 z/a" echo "100644 $o0 0 z/b" + echo "100644 $o0 0 z/c" echo "100644 $o1 0 z/d/e" ) >expected && git diff -u expected actual diff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh new file mode 100755 index 0000000000..b4760f2dc0 --- /dev/null +++ b/t/t5502-quickfetch.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +test_description='test quickfetch from local' + +. ./test-lib.sh + +test_expect_success setup ' + + test_tick && + echo ichi >file && + git add file && + git commit -m initial && + + cnt=$( ( + git count-objects | sed -e "s/ *objects,.*//" + ) ) && + test $cnt -eq 3 +' + +test_expect_success 'clone without alternate' ' + + ( + mkdir cloned && + cd cloned && + git init-db && + git remote add -f origin .. + ) && + cnt=$( ( + cd cloned && + git count-objects | sed -e "s/ *objects,.*//" + ) ) && + test $cnt -eq 3 +' + +test_expect_success 'further commits in the original' ' + + test_tick && + echo ni >file && + git commit -a -m second && + + cnt=$( ( + git count-objects | sed -e "s/ *objects,.*//" + ) ) && + test $cnt -eq 6 +' + +test_expect_success 'copy commit and tree but not blob by hand' ' + + git rev-list --objects HEAD | + git pack-objects --stdout | + ( + cd cloned && + git unpack-objects + ) && + + cnt=$( ( + cd cloned && + git count-objects | sed -e "s/ *objects,.*//" + ) ) && + test $cnt -eq 6 + + blob=$(git rev-parse HEAD:file | sed -e "s|..|&/|") && + test -f "cloned/.git/objects/$blob" && + rm -f "cloned/.git/objects/$blob" && + + cnt=$( ( + cd cloned && + git count-objects | sed -e "s/ *objects,.*//" + ) ) && + test $cnt -eq 5 + +' + +test_expect_success 'quickfetch should not leave a corrupted repository' ' + + ( + cd cloned && + git fetch + ) && + + cnt=$( ( + cd cloned && + git count-objects | sed -e "s/ *objects,.*//" + ) ) && + test $cnt -eq 6 + +' + +test_done |