From 365527adbfa8a16dfc05ab71818602f55ada55ba Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 12 Sep 2005 19:47:07 -0700 Subject: Fix CDPATH problem. CDPATH has two problems: * It takes scripts to unexpected places (somebody had CDPATH=..:../..:$HOME and the "cd" in git-clone.sh:get_repo_base took him to $HOME/.git when he said "clone foo bar" to clone a repository in "foo" which had "foo/.git"). CDPATH mechanism does not implicitly give "." at the beginning of CDPATH, which is the most irritating part. * The extra echo when it does its thing confuses scripts further. Most of our scripts that use "cd" includes git-sh-setup so the problem is primarily fixed there. git-clone starts without a repository, and it needs its own fix. Signed-off-by: Junio C Hamano 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 ] " exit 1 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 -- cgit v0.10.2-6-g49f6 From 22c67ca88ec65c4f278e8c82b2d98f3bd025238b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 12 Sep 2005 22:20:02 -0700 Subject: Propagate errors from fetch-pack correctly to git-fetch. When git-fetch-pack fails, the command does not notice the failure and instead pretended nothing was fetched and there was nothing wrong. Signed-off-by: Junio C Hamano 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 -- cgit v0.10.2-6-g49f6 From f88ed172e7a391bd907798ad2a3347a83cd24317 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 12 Sep 2005 22:20:42 -0700 Subject: Fix off-by-one error in git-merge 'git-merge -s' without a strategy name does not fail and does not give usage as it should. Signed-off-by: Junio C Hamano diff --git a/git-merge.sh b/git-merge.sh index a784e0f..e51e734 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -43,7 +43,7 @@ do case "$#,$1" in *,*=*) strategy=`expr "$1" : '-[^=]*=\(.*\)'` ;; - 0,*) + 1,*) usage ;; *) strategy="$2" -- cgit v0.10.2-6-g49f6 From e4cf17ce0db2dab7c9525a732f86c5e3df3b4ed0 Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Tue, 13 Sep 2005 08:22:26 +0200 Subject: [PATCH] Rename the 'fredrik' merge strategy to 'recursive'. Otherwise we would regret when Fredrik comes up with another merge algorithm with different pros-and-cons with the current one. Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano 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 9c2bdd6..a851f56 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-merge-fredrik.py b/git-merge-fredrik.py deleted file mode 100755 index 27b8486..0000000 --- a/git-merge-fredrik.py +++ /dev/null @@ -1,429 +0,0 @@ -#!/usr/bin/python - -import sys, math, random, os, re, signal, tempfile, stat, errno, traceback -from heapq import heappush, heappop -from sets import Set - -sys.path.append('@@GIT_PYTHON_PATH@@') -from gitMergeCommon import * - -alwaysWriteTree = False - -# The actual merge code -# --------------------- - -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.''' - assert(isinstance(h1, Commit) and isinstance(h2, Commit)) - assert(isinstance(graph, Graph)) - - def infoMsg(*args): - sys.stdout.write(' '*callDepth) - printList(args) - infoMsg('Merging:') - infoMsg(h1) - infoMsg(h2) - sys.stdout.flush() - - ca = getCommonAncestors(graph, h1, h2) - infoMsg('found', len(ca), 'common ancestor(s):') - for x in ca: - infoMsg(x) - sys.stdout.flush() - - Ms = ca[0] - for h in ca[1:]: - [Ms, ignore] = merge(Ms, h, - 'Temporary shared merge branch 1', - 'Temporary shared merge branch 2', - graph, callDepth+1) - 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 - else: - runProgram(['git-read-tree', h1.tree()]) - updateWd = False - cleanCache = True - - [shaRes, clean] = mergeTrees(h1.tree(), h2.tree(), Ms.tree(), - branch1Name, branch2Name, - cleanCache, updateWd) - - if clean or cleanCache: - res = Commit(None, [h1, h2], tree=shaRes) - graph.addNode(res) - else: - res = None - - return [res, clean] - -getFilesRE = re.compile('([0-9]+) ([a-z0-9]+) ([0-9a-f]{40})\t(.*)') -def getFilesAndDirs(tree): - files = Set() - dirs = Set() - out = runProgram(['git-ls-tree', '-r', '-z', tree]) - for l in out.split('\0'): - m = getFilesRE.match(l) - if m: - if m.group(2) == 'tree': - dirs.add(m.group(4)) - elif m.group(2) == 'blob': - files.add(m.group(4)) - - return [files, dirs] - -class CacheEntry: - def __init__(self, path): - class Stage: - def __init__(self): - self.sha1 = None - self.mode = None - - self.stages = [Stage(), Stage(), Stage()] - self.path = path - -unmergedRE = re.compile('^([0-9]+) ([0-9a-f]{40}) ([1-3])\t(.*)$') -def unmergedCacheEntries(): - '''Create a dictionary mapping file names to CacheEntry - objects. The dictionary contains one entry for every path with a - non-zero stage entry.''' - - lines = runProgram(['git-ls-files', '-z', '--unmerged']).split('\0') - lines.pop() - - res = {} - for l in lines: - m = unmergedRE.match(l) - if m: - mode = int(m.group(1), 8) - sha1 = m.group(2) - stage = int(m.group(3)) - 1 - path = m.group(4) - - if res.has_key(path): - e = res[path] - else: - e = CacheEntry(path) - res[path] = e - - e.stages[stage].mode = mode - e.stages[stage].sha1 = sha1 - else: - die('Error: Merge program failed: Unexpected output from', \ - 'git-ls-files:', l) - return res - -def mergeTrees(head, merge, common, branch1Name, branch2Name, - cleanCache, updateWd): - '''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) - where tree is the resulting tree and cleanMerge is True iff the - merge was clean.''' - - assert(isSha(head) and isSha(merge) and isSha(common)) - - if common == merge: - print 'Already uptodate!' - return [head, True] - - if updateWd: - updateArg = '-u' - else: - updateArg = '-i' - runProgram(['git-read-tree', updateArg, '-m', common, head, merge]) - cleanMerge = True - - [tree, code] = runProgram('git-write-tree', returnCode=True) - tree = tree.rstrip() - if code != 0: - [files, dirs] = getFilesAndDirs(head) - [filesM, dirsM] = getFilesAndDirs(merge) - files.union_update(filesM) - dirs.union_update(dirsM) - - cleanMerge = True - entries = unmergedCacheEntries() - for name in entries: - if not processEntry(entries[name], branch1Name, branch2Name, - files, dirs, cleanCache, updateWd): - cleanMerge = False - - if cleanMerge or cleanCache: - tree = runProgram('git-write-tree').rstrip() - else: - tree = None - else: - cleanMerge = True - - return [tree, cleanMerge] - -def processEntry(entry, branch1Name, branch2Name, files, dirs, - cleanCache, updateWd): - '''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 - -# 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]) - - if updateWd: - prog = ['git-cat-file', 'blob', sha] - if stat.S_ISREG(mode): - try: - os.unlink(path) - except OSError: - pass - if mode & 0100: - mode = 0777 - else: - mode = 0666 - fd = os.open(path, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, mode) - proc = subprocess.Popen(prog, stdout=fd) - proc.wait() - os.close(fd) - elif stat.S_ISLNK(mode): - linkTarget = runProgram(prog) - os.symlink(linkTarget, path) - else: - assert(False) - runProgram(['git-update-cache', '--', path]) - - def removeFile(clean, path): - if cleanCache or (not cleanCache and clean): - runProgram(['git-update-cache', '--force-remove', '--', path]) - - if updateWd: - try: - os.unlink(path) - except OSError, e: - if e.errno != errno.ENOENT and e.errno != errno.EISDIR: - raise - - def uniquePath(path, branch): - newPath = path + '_' + branch - suffix = 0 - while newPath in files or newPath in dirs: - suffix += 1 - newPath = path + '_' + branch + '_' + str(suffix) - files.add(newPath) - return newPath - - debug('processing', entry.path, 'clean cache:', cleanCache, - 'wd:', updateWd) - - cleanMerge = True - - path = entry.path - oSha = entry.stages[0].sha1 - oMode = entry.stages[0].mode - aSha = entry.stages[1].sha1 - aMode = entry.stages[1].mode - bSha = entry.stages[2].sha1 - bMode = entry.stages[2].mode - - assert(oSha == None or isSha(oSha)) - assert(aSha == None or isSha(aSha)) - assert(bSha == None or isSha(bSha)) - - assert(oMode == None or type(oMode) is int) - assert(aMode == None or type(aMode) is int) - assert(bMode == None or type(bMode) is int) - - if (oSha and (not aSha or not bSha)): - # - # Case A: Deleted in one - # - if (not aSha and not bSha) or \ - (aSha == oSha and not bSha) or \ - (not aSha and bSha == oSha): - # Deleted in both or deleted in one and unchanged in the other - if aSha: - print 'Removing ' + path - removeFile(True, path) - else: - # Deleted in one and changed in the other - cleanMerge = False - if not aSha: - print 'CONFLICT (del/mod): "' + path + '" deleted in', \ - branch1Name, 'and modified in', branch2Name, \ - '. Version', branch2Name, ' of "' + path + \ - '" left in tree' - mode = bMode - sha = bSha - else: - print 'CONFLICT (mod/del): "' + path + '" deleted in', \ - branch2Name, 'and modified in', branch1Name + \ - '. Version', branch1Name, 'of "' + path + \ - '" left in tree' - mode = aMode - sha = aSha - - updateFile(False, sha, mode, path) - - elif (not oSha and aSha and not bSha) or \ - (not oSha and not aSha and bSha): - # - # Case B: Added in one. - # - if aSha: - addBranch = branch1Name - otherBranch = branch2Name - mode = aMode - sha = aSha - conf = 'file/dir' - else: - addBranch = branch2Name - otherBranch = branch1Name - mode = bMode - sha = bSha - conf = 'dir/file' - - if path in dirs: - cleanMerge = False - newPath = uniquePath(path, addBranch) - print 'CONFLICT (' + conf + \ - '): There is a directory with name "' + path + '" in', \ - otherBranch + '. Adding "' + path + '" as "' + newPath + '"' - - removeFile(False, path) - path = newPath - else: - print 'Adding "' + path + '"' - - updateFile(True, sha, mode, path) - - elif not oSha and aSha and bSha: - # - # Case C: Added in both (check for same permissions). - # - if aSha == bSha: - 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 - print 'CONFLICT: adding with permission:', '0%o' % aMode - - updateFile(False, aSha, aMode, path) - else: - # This case is handled by git-read-tree - assert(False) - else: - cleanMerge = False - 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.' - removeFile(False, path) - updateFile(False, aSha, aMode, newPath1) - updateFile(False, bSha, bMode, newPath2) - - elif oSha and aSha and bSha: - # - # case D: Modified in both, but differently. - # - print 'Auto-merging', path - orig = runProgram(['git-unpack-file', oSha]).rstrip() - src1 = runProgram(['git-unpack-file', aSha]).rstrip() - src2 = runProgram(['git-unpack-file', bSha]).rstrip() - [out, ret] = runProgram(['merge', - '-L', branch1Name + '/' + path, - '-L', 'orig/' + path, - '-L', branch2Name + '/' + path, - src1, orig, src2], returnCode=True) - - if aMode == oMode: - mode = bMode - else: - mode = aMode - - sha = runProgram(['git-hash-object', '-t', 'blob', '-w', - src1]).rstrip() - - if ret != 0: - cleanMerge = False - print 'CONFLICT (content): Merge conflict in "' + path + '".' - updateFile(False, sha, mode, path) - else: - updateFile(True, sha, mode, path) - - os.unlink(orig) - os.unlink(src1) - os.unlink(src2) - else: - die("ERROR: Fatal merge failure, shouldn't happen.") - - return cleanMerge - -def usage(): - die('Usage:', sys.argv[0], ' ... -- ..') - -# main entry point as merge strategy module -# The first parameters up to -- are merge bases, and the rest are heads. -# This strategy module figures out merge bases itself, so we only -# get heads. - -if len(sys.argv) < 4: - usage() - -for nextArg in xrange(1, len(sys.argv)): - if sys.argv[nextArg] == '--': - if len(sys.argv) != nextArg + 3: - die('Not handling anything other than two heads merge.') - try: - h1 = firstBranch = sys.argv[nextArg + 1] - h2 = secondBranch = sys.argv[nextArg + 2] - except IndexError: - usage() - break - -print 'Merging', h1, 'with', h2 - -try: - h1 = runProgram(['git-rev-parse', '--verify', h1 + '^0']).rstrip() - h2 = runProgram(['git-rev-parse', '--verify', h2 + '^0']).rstrip() - - graph = buildGraph([h1, h2]) - - [res, clean] = merge(graph.shaMap[h1], graph.shaMap[h2], - firstBranch, secondBranch, graph) - - print '' -except: - traceback.print_exc(None, sys.stderr) - sys.exit(2) - -if clean: - sys.exit(0) -else: - print 'Automatic merge failed, fix up by hand' - sys.exit(1) diff --git a/git-merge-recursive.py b/git-merge-recursive.py new file mode 100755 index 0000000..27b8486 --- /dev/null +++ b/git-merge-recursive.py @@ -0,0 +1,429 @@ +#!/usr/bin/python + +import sys, math, random, os, re, signal, tempfile, stat, errno, traceback +from heapq import heappush, heappop +from sets import Set + +sys.path.append('@@GIT_PYTHON_PATH@@') +from gitMergeCommon import * + +alwaysWriteTree = False + +# The actual merge code +# --------------------- + +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.''' + assert(isinstance(h1, Commit) and isinstance(h2, Commit)) + assert(isinstance(graph, Graph)) + + def infoMsg(*args): + sys.stdout.write(' '*callDepth) + printList(args) + infoMsg('Merging:') + infoMsg(h1) + infoMsg(h2) + sys.stdout.flush() + + ca = getCommonAncestors(graph, h1, h2) + infoMsg('found', len(ca), 'common ancestor(s):') + for x in ca: + infoMsg(x) + sys.stdout.flush() + + Ms = ca[0] + for h in ca[1:]: + [Ms, ignore] = merge(Ms, h, + 'Temporary shared merge branch 1', + 'Temporary shared merge branch 2', + graph, callDepth+1) + 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 + else: + runProgram(['git-read-tree', h1.tree()]) + updateWd = False + cleanCache = True + + [shaRes, clean] = mergeTrees(h1.tree(), h2.tree(), Ms.tree(), + branch1Name, branch2Name, + cleanCache, updateWd) + + if clean or cleanCache: + res = Commit(None, [h1, h2], tree=shaRes) + graph.addNode(res) + else: + res = None + + return [res, clean] + +getFilesRE = re.compile('([0-9]+) ([a-z0-9]+) ([0-9a-f]{40})\t(.*)') +def getFilesAndDirs(tree): + files = Set() + dirs = Set() + out = runProgram(['git-ls-tree', '-r', '-z', tree]) + for l in out.split('\0'): + m = getFilesRE.match(l) + if m: + if m.group(2) == 'tree': + dirs.add(m.group(4)) + elif m.group(2) == 'blob': + files.add(m.group(4)) + + return [files, dirs] + +class CacheEntry: + def __init__(self, path): + class Stage: + def __init__(self): + self.sha1 = None + self.mode = None + + self.stages = [Stage(), Stage(), Stage()] + self.path = path + +unmergedRE = re.compile('^([0-9]+) ([0-9a-f]{40}) ([1-3])\t(.*)$') +def unmergedCacheEntries(): + '''Create a dictionary mapping file names to CacheEntry + objects. The dictionary contains one entry for every path with a + non-zero stage entry.''' + + lines = runProgram(['git-ls-files', '-z', '--unmerged']).split('\0') + lines.pop() + + res = {} + for l in lines: + m = unmergedRE.match(l) + if m: + mode = int(m.group(1), 8) + sha1 = m.group(2) + stage = int(m.group(3)) - 1 + path = m.group(4) + + if res.has_key(path): + e = res[path] + else: + e = CacheEntry(path) + res[path] = e + + e.stages[stage].mode = mode + e.stages[stage].sha1 = sha1 + else: + die('Error: Merge program failed: Unexpected output from', \ + 'git-ls-files:', l) + return res + +def mergeTrees(head, merge, common, branch1Name, branch2Name, + cleanCache, updateWd): + '''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) + where tree is the resulting tree and cleanMerge is True iff the + merge was clean.''' + + assert(isSha(head) and isSha(merge) and isSha(common)) + + if common == merge: + print 'Already uptodate!' + return [head, True] + + if updateWd: + updateArg = '-u' + else: + updateArg = '-i' + runProgram(['git-read-tree', updateArg, '-m', common, head, merge]) + cleanMerge = True + + [tree, code] = runProgram('git-write-tree', returnCode=True) + tree = tree.rstrip() + if code != 0: + [files, dirs] = getFilesAndDirs(head) + [filesM, dirsM] = getFilesAndDirs(merge) + files.union_update(filesM) + dirs.union_update(dirsM) + + cleanMerge = True + entries = unmergedCacheEntries() + for name in entries: + if not processEntry(entries[name], branch1Name, branch2Name, + files, dirs, cleanCache, updateWd): + cleanMerge = False + + if cleanMerge or cleanCache: + tree = runProgram('git-write-tree').rstrip() + else: + tree = None + else: + cleanMerge = True + + return [tree, cleanMerge] + +def processEntry(entry, branch1Name, branch2Name, files, dirs, + cleanCache, updateWd): + '''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 + +# 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]) + + if updateWd: + prog = ['git-cat-file', 'blob', sha] + if stat.S_ISREG(mode): + try: + os.unlink(path) + except OSError: + pass + if mode & 0100: + mode = 0777 + else: + mode = 0666 + fd = os.open(path, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, mode) + proc = subprocess.Popen(prog, stdout=fd) + proc.wait() + os.close(fd) + elif stat.S_ISLNK(mode): + linkTarget = runProgram(prog) + os.symlink(linkTarget, path) + else: + assert(False) + runProgram(['git-update-cache', '--', path]) + + def removeFile(clean, path): + if cleanCache or (not cleanCache and clean): + runProgram(['git-update-cache', '--force-remove', '--', path]) + + if updateWd: + try: + os.unlink(path) + except OSError, e: + if e.errno != errno.ENOENT and e.errno != errno.EISDIR: + raise + + def uniquePath(path, branch): + newPath = path + '_' + branch + suffix = 0 + while newPath in files or newPath in dirs: + suffix += 1 + newPath = path + '_' + branch + '_' + str(suffix) + files.add(newPath) + return newPath + + debug('processing', entry.path, 'clean cache:', cleanCache, + 'wd:', updateWd) + + cleanMerge = True + + path = entry.path + oSha = entry.stages[0].sha1 + oMode = entry.stages[0].mode + aSha = entry.stages[1].sha1 + aMode = entry.stages[1].mode + bSha = entry.stages[2].sha1 + bMode = entry.stages[2].mode + + assert(oSha == None or isSha(oSha)) + assert(aSha == None or isSha(aSha)) + assert(bSha == None or isSha(bSha)) + + assert(oMode == None or type(oMode) is int) + assert(aMode == None or type(aMode) is int) + assert(bMode == None or type(bMode) is int) + + if (oSha and (not aSha or not bSha)): + # + # Case A: Deleted in one + # + if (not aSha and not bSha) or \ + (aSha == oSha and not bSha) or \ + (not aSha and bSha == oSha): + # Deleted in both or deleted in one and unchanged in the other + if aSha: + print 'Removing ' + path + removeFile(True, path) + else: + # Deleted in one and changed in the other + cleanMerge = False + if not aSha: + print 'CONFLICT (del/mod): "' + path + '" deleted in', \ + branch1Name, 'and modified in', branch2Name, \ + '. Version', branch2Name, ' of "' + path + \ + '" left in tree' + mode = bMode + sha = bSha + else: + print 'CONFLICT (mod/del): "' + path + '" deleted in', \ + branch2Name, 'and modified in', branch1Name + \ + '. Version', branch1Name, 'of "' + path + \ + '" left in tree' + mode = aMode + sha = aSha + + updateFile(False, sha, mode, path) + + elif (not oSha and aSha and not bSha) or \ + (not oSha and not aSha and bSha): + # + # Case B: Added in one. + # + if aSha: + addBranch = branch1Name + otherBranch = branch2Name + mode = aMode + sha = aSha + conf = 'file/dir' + else: + addBranch = branch2Name + otherBranch = branch1Name + mode = bMode + sha = bSha + conf = 'dir/file' + + if path in dirs: + cleanMerge = False + newPath = uniquePath(path, addBranch) + print 'CONFLICT (' + conf + \ + '): There is a directory with name "' + path + '" in', \ + otherBranch + '. Adding "' + path + '" as "' + newPath + '"' + + removeFile(False, path) + path = newPath + else: + print 'Adding "' + path + '"' + + updateFile(True, sha, mode, path) + + elif not oSha and aSha and bSha: + # + # Case C: Added in both (check for same permissions). + # + if aSha == bSha: + 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 + print 'CONFLICT: adding with permission:', '0%o' % aMode + + updateFile(False, aSha, aMode, path) + else: + # This case is handled by git-read-tree + assert(False) + else: + cleanMerge = False + 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.' + removeFile(False, path) + updateFile(False, aSha, aMode, newPath1) + updateFile(False, bSha, bMode, newPath2) + + elif oSha and aSha and bSha: + # + # case D: Modified in both, but differently. + # + print 'Auto-merging', path + orig = runProgram(['git-unpack-file', oSha]).rstrip() + src1 = runProgram(['git-unpack-file', aSha]).rstrip() + src2 = runProgram(['git-unpack-file', bSha]).rstrip() + [out, ret] = runProgram(['merge', + '-L', branch1Name + '/' + path, + '-L', 'orig/' + path, + '-L', branch2Name + '/' + path, + src1, orig, src2], returnCode=True) + + if aMode == oMode: + mode = bMode + else: + mode = aMode + + sha = runProgram(['git-hash-object', '-t', 'blob', '-w', + src1]).rstrip() + + if ret != 0: + cleanMerge = False + print 'CONFLICT (content): Merge conflict in "' + path + '".' + updateFile(False, sha, mode, path) + else: + updateFile(True, sha, mode, path) + + os.unlink(orig) + os.unlink(src1) + os.unlink(src2) + else: + die("ERROR: Fatal merge failure, shouldn't happen.") + + return cleanMerge + +def usage(): + die('Usage:', sys.argv[0], ' ... -- ..') + +# main entry point as merge strategy module +# The first parameters up to -- are merge bases, and the rest are heads. +# This strategy module figures out merge bases itself, so we only +# get heads. + +if len(sys.argv) < 4: + usage() + +for nextArg in xrange(1, len(sys.argv)): + if sys.argv[nextArg] == '--': + if len(sys.argv) != nextArg + 3: + die('Not handling anything other than two heads merge.') + try: + h1 = firstBranch = sys.argv[nextArg + 1] + h2 = secondBranch = sys.argv[nextArg + 2] + except IndexError: + usage() + break + +print 'Merging', h1, 'with', h2 + +try: + h1 = runProgram(['git-rev-parse', '--verify', h1 + '^0']).rstrip() + h2 = runProgram(['git-rev-parse', '--verify', h2 + '^0']).rstrip() + + graph = buildGraph([h1, h2]) + + [res, clean] = merge(graph.shaMap[h1], graph.shaMap[h2], + firstBranch, secondBranch, graph) + + print '' +except: + traceback.print_exc(None, sys.stderr) + sys.exit(2) + +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 e51e734..818e6b7 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -12,9 +12,9 @@ usage () { die "git-merge [-n] [-s ]... +" } -# 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= -- cgit v0.10.2-6-g49f6 From a5cd85e084458bf573d0d29fa79c4e1ad7973ed7 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 13 Sep 2005 13:16:45 -0700 Subject: Detect ls-remote failure properly. The part that can fail is before the pipe, so we need to propagate the error properly to the main process. Signed-off-by: Junio C Hamano 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 ;; -- cgit v0.10.2-6-g49f6 From ccfd3e99621f489db4ea37a03be7674adbd03f6e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 13 Sep 2005 00:05:22 -0700 Subject: [PATCH] Define relative .git/objects/info/alternates semantics. An entry in the alternates file can name a directory relative to the object store it describes. A typical linux-2.6 maintainer repository would have "../../../torvalds/linux-2.6.git/objects" there, because the subsystem maintainer object store would live in /pub/scm/linux/kernel/git/$u/$system.git/objects/ and the object store of Linus tree is in /pub/scm/linux/kernel/git/torvalds/linux-2.6.git/objects/ This unfortunately is different from GIT_ALTERNATE_OBJECT_DIRECTORIES which is relative to the cwd of the running process, but there is no way to make it consistent with the behaviour of the environment variable. The process typically is run in $system.git/ directory for a naked repository, or one level up for a repository with a working tree, so we just define it to be relative to the objects/ directory to be different from either ;-). Later, the dumb transport could be updated to read from info/alternates and make requests for the repository the repository borrows from. Signed-off-by: Junio C Hamano 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); } -- cgit v0.10.2-6-g49f6 From d9a23fa6f7124befe7803f5f3a8c53999578caa4 Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Tue, 13 Sep 2005 23:39:34 +0200 Subject: [PATCH] Be more like the 'resolve' strategy. If there are non-mergeable changes leave the head contents in the cache and update the working directory with the output from merge(1). In the add/add and delete/modify conflict cases leave unmerged cache entries in the index. Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano diff --git a/git-merge-recursive.py b/git-merge-recursive.py index 27b8486..145a5cf 100755 --- a/git-merge-recursive.py +++ b/git-merge-recursive.py @@ -7,8 +7,6 @@ from sets import Set sys.path.append('@@GIT_PYTHON_PATH@@') from gitMergeCommon import * -alwaysWriteTree = False - # The actual merge code # --------------------- @@ -46,19 +44,14 @@ def merge(h1, h2, branch1Name, branch2Name, graph, callDepth=0): 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 + cleanCache = False else: 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) @@ -125,7 +118,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 +131,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 +151,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 +163,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 +203,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-cache', '--add', '--', path]) + elif updateCache: + runProgram(['git-update-cache', '--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]) - if updateWd: + if not cleanCache and clean: try: os.unlink(path) except OSError, e: @@ -235,8 +230,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 +321,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 +335,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 +365,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) -- cgit v0.10.2-6-g49f6 From 0d214b6619bf14a6409a2a1ba079f3530d40ee41 Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Tue, 13 Sep 2005 23:40:23 +0200 Subject: [PATCH] Don't output 'Automatic merge failed, ...' git-merge.sh does this for us. Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano diff --git a/git-merge-recursive.py b/git-merge-recursive.py index 145a5cf..eeb3d24 100755 --- a/git-merge-recursive.py +++ b/git-merge-recursive.py @@ -423,5 +423,4 @@ except: if clean: sys.exit(0) else: - print 'Automatic merge failed, fix up by hand' sys.exit(1) -- cgit v0.10.2-6-g49f6 From 87a71b65acc10c4879b13d2030185e5640a087cd Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Tue, 13 Sep 2005 23:40:47 +0200 Subject: [PATCH] Adjust git-merge-recursive.py for the new tool names. Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano diff --git a/git-merge-recursive.py b/git-merge-recursive.py index eeb3d24..ea7f994 100755 --- a/git-merge-recursive.py +++ b/git-merge-recursive.py @@ -41,7 +41,7 @@ def merge(h1, h2, branch1Name, branch2Name, graph, callDepth=0): if callDepth == 0: if len(ca) > 1: runProgram(['git-read-tree', h1.tree()]) - runProgram(['git-update-cache', '-q', '--refresh']) + runProgram(['git-update-index', '-q', '--refresh']) # Use the original index if we only have one common ancestor cleanCache = False @@ -205,14 +205,14 @@ def processEntry(entry, branch1Name, branch2Name, files, dirs, cleanCache): assert(False) if updateWd and updateCache: - runProgram(['git-update-cache', '--add', '--', path]) + runProgram(['git-update-index', '--add', '--', path]) elif updateCache: - runProgram(['git-update-cache', '--add', '--cacheinfo', + 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 not cleanCache and clean: try: -- cgit v0.10.2-6-g49f6 From 6511cce24b932eba33d8e13b54664673121a8b7e Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Tue, 13 Sep 2005 23:41:16 +0200 Subject: [PATCH] Use a temporary index file when we merge the common ancestors. With this change we can get rid of a call to 'git-update-index --refresh'. Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano diff --git a/git-merge-recursive.py b/git-merge-recursive.py index ea7f994..66165b0 100755 --- a/git-merge-recursive.py +++ b/git-merge-recursive.py @@ -10,6 +10,22 @@ from gitMergeCommon import * # 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.''' @@ -39,13 +55,10 @@ 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-index', '-q', '--refresh']) - # Use the original index if we only have one common ancestor - + setupIndex(False) cleanCache = False else: + setupIndex(True) runProgram(['git-read-tree', h1.tree()]) cleanCache = True -- cgit v0.10.2-6-g49f6 From daae66994967ba503f5e868eddf6962a2c1d7871 Mon Sep 17 00:00:00 2001 From: Fredrik Kuivinen Date: Tue, 13 Sep 2005 23:41:39 +0200 Subject: [PATCH] Use the 'die' function where it is appropriate. Signed-off-by: Fredrik Kuivinen Signed-off-by: Junio C Hamano 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 # ----------------- -- cgit v0.10.2-6-g49f6 From 74376a686425c6304eafff9f26d8d5fb0d083bc0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 13 Sep 2005 21:09:03 -0700 Subject: git-merge-recursive: Trivial RE fixes. Signed-off-by: Junio C Hamano diff --git a/git-merge-recursive.py b/git-merge-recursive.py index 66165b0..60e8b21 100755 --- a/git-merge-recursive.py +++ b/git-merge-recursive.py @@ -74,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() @@ -99,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 -- cgit v0.10.2-6-g49f6 From 2ba6c47be1762726ad0c1d5779064c489150d789 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 13 Sep 2005 22:27:42 -0700 Subject: read-tree: fix bogus debugging statement. We wanted to detect case #16 which should be rare, but botched the case when some paths are missing, causing a segfault. My fault. Signed-off-by: Junio C Hamano 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 -- cgit v0.10.2-6-g49f6