summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes-1.5.1.2.txt27
-rw-r--r--Documentation/git-archive.txt5
-rw-r--r--Documentation/git-cherry-pick.txt2
-rw-r--r--Documentation/git-config.txt24
-rw-r--r--Documentation/git-tar-tree.txt2
-rw-r--r--Documentation/tutorial.txt2
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--builtin-config.c2
-rw-r--r--builtin-fetch--tool.c84
-rw-r--r--builtin-rev-list.c4
-rw-r--r--builtin-update-index.c2
-rw-r--r--contrib/emacs/git.el42
-rwxr-xr-xcontrib/gitview/gitview260
-rw-r--r--contrib/hooks/update-paranoid284
-rw-r--r--convert-objects.c2
-rw-r--r--decorate.c1
-rwxr-xr-xgit-clone.sh2
-rwxr-xr-xgit-fetch.sh42
-rw-r--r--merge-recursive.c119
-rw-r--r--perl/Makefile.PL2
-rwxr-xr-xt/t3030-merge-recursive.sh104
-rwxr-xr-xt/t5502-quickfetch.sh89
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