summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile2
-rwxr-xr-xgit-clone.sh3
-rwxr-xr-xgit-fetch.sh11
-rwxr-xr-xgit-ls-remote.sh15
-rwxr-xr-xgit-merge-recursive.py (renamed from git-merge-fredrik.py)98
-rwxr-xr-xgit-merge.sh6
-rwxr-xr-xgit-sh-setup.sh6
-rw-r--r--gitMergeCommon.py27
-rw-r--r--read-tree.c15
-rw-r--r--sha1_file.c28
11 files changed, 133 insertions, 80 deletions
diff --git a/.gitignore b/.gitignore
index cc2b29e..31c03f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,10 +44,10 @@ git-mailinfo
git-mailsplit
git-merge
git-merge-base
-git-merge-fredrik
git-merge-index
git-merge-octopus
git-merge-one-file
+git-merge-recursive
git-merge-resolve
git-merge-stupid
git-mktag
diff --git a/Makefile b/Makefile
index e2bbcb5..9725b1a 100644
--- a/Makefile
+++ b/Makefile
@@ -83,7 +83,7 @@ SCRIPT_PERL = \
git-rename.perl git-shortlog.perl
SCRIPT_PYTHON = \
- git-merge-fredrik.py
+ git-merge-recursive.py
# The ones that do not have to link with lcrypto nor lz.
SIMPLE_PROGRAMS = \
diff --git a/git-clone.sh b/git-clone.sh
index c076292..a21f13a 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -5,6 +5,9 @@
#
# Clone a repository into a different directory that does not yet exist.
+# See git-sh-setup why.
+unset CDPATH
+
usage() {
echo >&2 "* git clone [-l [-s]] [-q] [-u <upload-pack>] <repo> <dir>"
exit 1
diff --git a/git-fetch.sh b/git-fetch.sh
index 673a184..2273944 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -205,9 +205,16 @@ case "$remote" in
http://* | https://* | rsync://* )
;; # we are already done.
*)
- git-fetch-pack "$remote" $rref |
+ (
+ git-fetch-pack "$remote" $rref || echo failed "$remote"
+ ) |
while read sha1 remote_name
do
+ case "$sha1" in
+ failed)
+ echo >&2 "Fetch failure: $remote"
+ exit 1 ;;
+ esac
found=
single_force=
for ref in $refs
@@ -225,7 +232,7 @@ http://* | https://* | rsync://* )
local_name=$(expr "$found" : '[^:]*:\(.*\)')
append_fetch_head "$sha1" "$remote" "$remote_name" "$remote_nick" "$local_name"
- done
+ done || exit
;;
esac
diff --git a/git-ls-remote.sh b/git-ls-remote.sh
index 604c5f5..bfbd5a4 100755
--- a/git-ls-remote.sh
+++ b/git-ls-remote.sh
@@ -42,12 +42,16 @@ http://* | https://* )
if [ -n "$GIT_SSL_NO_VERIFY" ]; then
curl_extra_args="-k"
fi
- curl -nsf $curl_extra_args "$peek_repo/info/refs" || exit 1
+ curl -nsf $curl_extra_args "$peek_repo/info/refs" ||
+ echo "failed slurping"
;;
rsync://* )
mkdir $tmpdir
- rsync -rq "$peek_repo/refs" $tmpdir || exit 1
+ rsync -rq "$peek_repo/refs" $tmpdir || {
+ echo "failed slurping"
+ exit
+ }
(cd $tmpdir && find refs -type f) |
while read path
do
@@ -58,12 +62,17 @@ rsync://* )
;;
* )
- git-peek-remote "$peek_repo"
+ git-peek-remote "$peek_repo" ||
+ echo "failed slurping"
;;
esac |
sort -t ' ' -k 2 |
while read sha1 path
do
+ case "$sha1" in
+ failed)
+ die "Failed to find remote refs"
+ esac
case "$path" in
refs/heads/*)
group=heads ;;
diff --git a/git-merge-fredrik.py b/git-merge-recursive.py
index 27b8486..60e8b21 100755
--- a/git-merge-fredrik.py
+++ b/git-merge-recursive.py
@@ -7,11 +7,25 @@ from sets import Set
sys.path.append('@@GIT_PYTHON_PATH@@')
from gitMergeCommon import *
-alwaysWriteTree = False
-
# The actual merge code
# ---------------------
+originalIndexFile = os.environ.get('GIT_INDEX_FILE',
+ os.environ.get('GIT_DIR', '.git') + '/index')
+temporaryIndexFile = os.environ.get('GIT_DIR', '.git') + \
+ '/merge-recursive-tmp-index'
+def setupIndex(temporary):
+ try:
+ os.unlink(temporaryIndexFile)
+ except OSError:
+ pass
+ if temporary:
+ newIndex = temporaryIndexFile
+ os.environ
+ else:
+ newIndex = originalIndexFile
+ os.environ['GIT_INDEX_FILE'] = newIndex
+
def merge(h1, h2, branch1Name, branch2Name, graph, callDepth=0):
'''Merge the commits h1 and h2, return the resulting virtual
commit object and a flag indicating the cleaness of the merge.'''
@@ -41,24 +55,16 @@ def merge(h1, h2, branch1Name, branch2Name, graph, callDepth=0):
assert(isinstance(Ms, Commit))
if callDepth == 0:
- if len(ca) > 1:
- runProgram(['git-read-tree', h1.tree()])
- runProgram(['git-update-cache', '-q', '--refresh'])
- # Use the original index if we only have one common ancestor
-
- updateWd = True
- if alwaysWriteTree:
- cleanCache = True
- else:
- cleanCache = False
+ setupIndex(False)
+ cleanCache = False
else:
+ setupIndex(True)
runProgram(['git-read-tree', h1.tree()])
- updateWd = False
cleanCache = True
[shaRes, clean] = mergeTrees(h1.tree(), h2.tree(), Ms.tree(),
branch1Name, branch2Name,
- cleanCache, updateWd)
+ cleanCache)
if clean or cleanCache:
res = Commit(None, [h1, h2], tree=shaRes)
@@ -68,7 +74,7 @@ def merge(h1, h2, branch1Name, branch2Name, graph, callDepth=0):
return [res, clean]
-getFilesRE = re.compile('([0-9]+) ([a-z0-9]+) ([0-9a-f]{40})\t(.*)')
+getFilesRE = re.compile(r'^([0-7]+) (\S+) ([0-9a-f]{40})\t(.*)$', re.S)
def getFilesAndDirs(tree):
files = Set()
dirs = Set()
@@ -93,7 +99,7 @@ class CacheEntry:
self.stages = [Stage(), Stage(), Stage()]
self.path = path
-unmergedRE = re.compile('^([0-9]+) ([0-9a-f]{40}) ([1-3])\t(.*)$')
+unmergedRE = re.compile(r'^([0-7]+) ([0-9a-f]{40}) ([1-3])\t(.*)$', re.S)
def unmergedCacheEntries():
'''Create a dictionary mapping file names to CacheEntry
objects. The dictionary contains one entry for every path with a
@@ -125,7 +131,7 @@ def unmergedCacheEntries():
return res
def mergeTrees(head, merge, common, branch1Name, branch2Name,
- cleanCache, updateWd):
+ cleanCache):
'''Merge the trees 'head' and 'merge' with the common ancestor
'common'. The name of the head branch is 'branch1Name' and the name of
the merge branch is 'branch2Name'. Return a tuple (tree, cleanMerge)
@@ -138,10 +144,11 @@ def mergeTrees(head, merge, common, branch1Name, branch2Name,
print 'Already uptodate!'
return [head, True]
- if updateWd:
- updateArg = '-u'
- else:
+ if cleanCache:
updateArg = '-i'
+ else:
+ updateArg = '-u'
+
runProgram(['git-read-tree', updateArg, '-m', common, head, merge])
cleanMerge = True
@@ -157,7 +164,7 @@ def mergeTrees(head, merge, common, branch1Name, branch2Name,
entries = unmergedCacheEntries()
for name in entries:
if not processEntry(entries[name], branch1Name, branch2Name,
- files, dirs, cleanCache, updateWd):
+ files, dirs, cleanCache):
cleanMerge = False
if cleanMerge or cleanCache:
@@ -169,29 +176,25 @@ def mergeTrees(head, merge, common, branch1Name, branch2Name,
return [tree, cleanMerge]
-def processEntry(entry, branch1Name, branch2Name, files, dirs,
- cleanCache, updateWd):
+def processEntry(entry, branch1Name, branch2Name, files, dirs, cleanCache):
'''Merge one cache entry. 'files' is a Set with the files in both of
the heads that we are going to merge. 'dirs' contains the
corresponding data for directories. If 'cleanCache' is True no
non-zero stages will be left in the cache for the path
corresponding to the entry 'entry'.'''
-# cleanCache == True => Don't leave any non-stage 0 entries in the cache.
-# False => Leave unmerged entries
-
-# updateWd == True => Update the working directory to correspond to the cache
-# False => Leave the working directory unchanged
+# cleanCache == True => Don't leave any non-stage 0 entries in the cache and
+# don't update the working directory
+# False => Leave unmerged entries and update the working directory
# clean == True => non-conflict case
# False => conflict case
# If cleanCache == False then the cache shouldn't be updated if clean == False
- def updateFile(clean, sha, mode, path):
- if cleanCache or (not cleanCache and clean):
- runProgram(['git-update-cache', '--add', '--cacheinfo',
- '0%o' % mode, sha, path])
+ def updateFile(clean, sha, mode, path, onlyWd=False):
+ updateCache = not onlyWd and (cleanCache or (not cleanCache and clean))
+ updateWd = onlyWd or (not cleanCache and clean)
if updateWd:
prog = ['git-cat-file', 'blob', sha]
@@ -213,13 +216,18 @@ def processEntry(entry, branch1Name, branch2Name, files, dirs,
os.symlink(linkTarget, path)
else:
assert(False)
- runProgram(['git-update-cache', '--', path])
+
+ if updateWd and updateCache:
+ runProgram(['git-update-index', '--add', '--', path])
+ elif updateCache:
+ runProgram(['git-update-index', '--add', '--cacheinfo',
+ '0%o' % mode, sha, path])
def removeFile(clean, path):
if cleanCache or (not cleanCache and clean):
- runProgram(['git-update-cache', '--force-remove', '--', path])
+ runProgram(['git-update-index', '--force-remove', '--', path])
- if updateWd:
+ if not cleanCache and clean:
try:
os.unlink(path)
except OSError, e:
@@ -235,8 +243,7 @@ def processEntry(entry, branch1Name, branch2Name, files, dirs,
files.add(newPath)
return newPath
- debug('processing', entry.path, 'clean cache:', cleanCache,
- 'wd:', updateWd)
+ debug('processing', entry.path, 'clean cache:', cleanCache)
cleanMerge = True
@@ -327,9 +334,9 @@ def processEntry(entry, branch1Name, branch2Name, files, dirs,
if aMode != bMode:
cleanMerge = False
print 'CONFLICT: File "' + path + \
- '" added identically in both branches,'
- print 'CONFLICT: but permissions conflict', '0%o' % aMode, \
- '->', '0%o' % bMode
+ '" added identically in both branches,', \
+ 'but permissions conflict', '0%o' % aMode, '->', \
+ '0%o' % bMode
print 'CONFLICT: adding with permission:', '0%o' % aMode
updateFile(False, aSha, aMode, path)
@@ -341,8 +348,7 @@ def processEntry(entry, branch1Name, branch2Name, files, dirs,
newPath1 = uniquePath(path, branch1Name)
newPath2 = uniquePath(path, branch2Name)
print 'CONFLICT (add/add): File "' + path + \
- '" added non-identically in both branches.', \
- 'Adding "' + newPath1 + '" and "' + newPath2 + '" instead.'
+ '" added non-identically in both branches.'
removeFile(False, path)
updateFile(False, aSha, aMode, newPath1)
updateFile(False, bSha, bMode, newPath2)
@@ -372,7 +378,12 @@ def processEntry(entry, branch1Name, branch2Name, files, dirs,
if ret != 0:
cleanMerge = False
print 'CONFLICT (content): Merge conflict in "' + path + '".'
- updateFile(False, sha, mode, path)
+
+ if cleanCache:
+ updateFile(False, sha, mode, path)
+ else:
+ updateFile(True, aSha, aMode, path)
+ updateFile(False, sha, mode, path, True)
else:
updateFile(True, sha, mode, path)
@@ -425,5 +436,4 @@ except:
if clean:
sys.exit(0)
else:
- print 'Automatic merge failed, fix up by hand'
sys.exit(1)
diff --git a/git-merge.sh b/git-merge.sh
index a784e0f..818e6b7 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -12,9 +12,9 @@ usage () {
die "git-merge [-n] [-s <strategy>]... <merge-message> <head> <remote>+"
}
-# all_strategies='resolve fredrik stupid octopus'
+# all_strategies='resolve recursive stupid octopus'
-all_strategies='fredrik octopus resolve stupid'
+all_strategies='recursive octopus resolve stupid'
default_strategies='resolve octopus'
use_strategies=
@@ -43,7 +43,7 @@ do
case "$#,$1" in
*,*=*)
strategy=`expr "$1" : '-[^=]*=\(.*\)'` ;;
- 0,*)
+ 1,*)
usage ;;
*)
strategy="$2"
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index 06d8299..d5bfa62 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -6,6 +6,12 @@
: ${GIT_DIR=.git}
: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
+# Having this variable in your environment would break scripts because
+# you would cause "cd" to be be taken to unexpected places. If you
+# like CDPATH, define it for your interactive shell sessions without
+# exporting it.
+unset CDPATH
+
die() {
echo "$@" >&2
exit 1
diff --git a/gitMergeCommon.py b/gitMergeCommon.py
index ce9694b..3af4fbd 100644
--- a/gitMergeCommon.py
+++ b/gitMergeCommon.py
@@ -1,19 +1,24 @@
import sys, re, os, traceback
from sets import Set
+def die(*args):
+ printList(args, sys.stderr)
+ sys.exit(2)
+
+def printList(list, file=sys.stdout):
+ for x in list:
+ file.write(str(x))
+ file.write(' ')
+ file.write('\n')
+
if sys.version_info[0] < 2 or \
(sys.version_info[0] == 2 and sys.version_info[1] < 4):
- print 'Python version 2.4 required, found', \
- str(sys.version_info[0])+'.'+str(sys.version_info[1])+'.'+ \
- str(sys.version_info[2])
- sys.exit(1)
+ die('Python version 2.4 required, found', \
+ str(sys.version_info[0])+'.'+str(sys.version_info[1])+'.'+ \
+ str(sys.version_info[2]))
import subprocess
-def die(*args):
- printList(args, sys.stderr)
- sys.exit(2)
-
# Debugging machinery
# -------------------
@@ -32,12 +37,6 @@ def debug(*args):
if funcName in functionsToDebug:
printList(args)
-def printList(list, file=sys.stdout):
- for x in list:
- file.write(str(x))
- file.write(' ')
- file.write('\n')
-
# Program execution
# -----------------
diff --git a/read-tree.c b/read-tree.c
index 4db154d..6a5c08c 100644
--- a/read-tree.c
+++ b/read-tree.c
@@ -362,12 +362,15 @@ static int keep_entry(struct cache_entry *ce)
static void show_stage_entry(FILE *o,
const char *label, const struct cache_entry *ce)
{
- fprintf(stderr, "%s%06o %s %d\t%s\n",
- label,
- ntohl(ce->ce_mode),
- sha1_to_hex(ce->sha1),
- ce_stage(ce),
- ce->name);
+ if (!ce)
+ fprintf(o, "%s (missing)\n", label);
+ else
+ fprintf(o, "%s%06o %s %d\t%s\n",
+ label,
+ ntohl(ce->ce_mode),
+ sha1_to_hex(ce->sha1),
+ ce_stage(ce),
+ ce->name);
}
#endif
diff --git a/sha1_file.c b/sha1_file.c
index f4c742e..6638202 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -240,10 +240,12 @@ static struct alternate_object_database **alt_odb_tail;
* SHA1, an extra slash for the first level indirection, and the
* terminating NUL.
*/
-static void link_alt_odb_entries(const char *alt, const char *ep, int sep)
+static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
+ const char *relative_base)
{
const char *cp, *last;
struct alternate_object_database *ent;
+ int base_len = -1;
last = alt;
while (last < ep) {
@@ -261,12 +263,25 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep)
int pfxlen = cp - last;
int entlen = pfxlen + 43;
+ if (*last != '/' && relative_base) {
+ /* Relative alt-odb */
+ if (base_len < 0)
+ base_len = strlen(relative_base) + 1;
+ entlen += base_len;
+ pfxlen += base_len;
+ }
ent = xmalloc(sizeof(*ent) + entlen);
*alt_odb_tail = ent;
alt_odb_tail = &(ent->next);
ent->next = NULL;
-
- memcpy(ent->base, last, pfxlen);
+ if (*last != '/' && relative_base) {
+ memcpy(ent->base, relative_base, base_len - 1);
+ ent->base[base_len - 1] = '/';
+ memcpy(ent->base + base_len,
+ last, cp - last);
+ }
+ else
+ memcpy(ent->base, last, pfxlen);
ent->name = ent->base + pfxlen + 1;
ent->base[pfxlen] = ent->base[pfxlen + 3] = '/';
ent->base[entlen-1] = 0;
@@ -288,12 +303,12 @@ void prepare_alt_odb(void)
alt = getenv(ALTERNATE_DB_ENVIRONMENT);
if (!alt) alt = "";
- sprintf(path, "%s/info/alternates", get_object_directory());
if (alt_odb_tail)
return;
alt_odb_tail = &alt_odb_list;
- link_alt_odb_entries(alt, alt + strlen(alt), ':');
+ link_alt_odb_entries(alt, alt + strlen(alt), ':', NULL);
+ sprintf(path, "%s/info/alternates", get_object_directory());
fd = open(path, O_RDONLY);
if (fd < 0)
return;
@@ -306,7 +321,8 @@ void prepare_alt_odb(void)
if (map == MAP_FAILED)
return;
- link_alt_odb_entries(map, map + st.st_size, '\n');
+ link_alt_odb_entries(map, map + st.st_size, '\n',
+ get_object_directory());
munmap(map, st.st_size);
}