From 16d6b8ab6fd7f68bfd9f4d312965cb99e8ad911b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 00:16:59 +0100 Subject: Initial import of a python script to import changesets from Perforce into git. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 150 ++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 contrib/fast-import/p4-fast-export.py (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py new file mode 100644 index 0000000000..238f6e3524 --- /dev/null +++ b/contrib/fast-import/p4-fast-export.py @@ -0,0 +1,150 @@ +#!/usr/bin/python +# +# p4-fast-export.py +# +# Author: Simon Hausmann +# License: MIT +# +# TODO: - fix date parsing (how hard can it be?) +# - support integrations (at least p4i) +# - support incremental imports +# - create tags +# - instead of reading all files into a variable try to pipe from +# - p4 print directly to stdout. need to figure out file size somehow +# though. +# - support p4 submit (hah!) +# - don't hardcode the import to master +# +import os, string, sys + +# yep, that's hardcoded right. will fix to a commandline option rsn :) +prefix = "//depot/qt/main/" +# that's in revision range syntax, for example @2342,523634 +changeRange = "" + +def describe(change): + output = os.popen("p4 describe %s" % change).readlines() + + firstLine = output[0] + + author = firstLine.split(" ")[3] + author = author[:author.find("@")] + + filesSection = 0 + try: + filesSection = output.index("Affected files ...\n") + except ValueError: + sys.stderr.write("Change %s doesn't seem to affect any files. Weird.\n" % change) + return [], [], [], [] + + differencesSection = 0 + try: + differencesSection = output.index("Differences ...\n") + except ValueError: + sys.stderr.write("Change %s doesn't seem to have a differences section. Weird.\n" % change) + return [], [], [], [] + + log = output[2:filesSection - 1] + + lines = output[filesSection + 2:differencesSection - 1] + + changed = [] + removed = [] + + for line in lines: + # chop off "... " and trailing newline + line = line[4:len(line) - 1] + + lastSpace = line.rfind(" ") + if lastSpace == -1: + sys.stderr.write("trouble parsing line %s, skipping!\n" % line) + continue + + operation = line[lastSpace + 1:] + path = line[:lastSpace] + + if operation == "delete": + removed.append(path) + else: + changed.append(path) + + return author, log, changed, removed + +def p4cat(path): + return os.popen("p4 print -q \"%s\"" % path).read() + +def stripRevision(path): + hashPos = path.rindex("#") + return path[:hashPos] + +def getUserMap(): + users = {} + output = os.popen("p4 users") + for line in output: + firstSpace = line.index(" ") + secondSpace = line.index(" ", firstSpace + 1) + key = line[:firstSpace] + email = line[firstSpace + 1:secondSpace] + openParenPos = line.index("(", secondSpace) + closedParenPos = line.index(")", openParenPos) + name = line[openParenPos + 1:closedParenPos] + + users[key] = name + " " + email + + return users + + +users = getUserMap() + +output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() + +changes = [] +for line in output: + changeNum = line.split(" ")[1] + changes.append(changeNum) + +changes.reverse() + +sys.stderr.write("\n") + +cnt = 0 +for change in changes: + [ author, log, changedFiles, removedFiles ] = describe(change) + sys.stderr.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + cnt = cnt + 1 +# sys.stderr.write("%s\n" % log) +# sys.stderr.write("%s\n" % changedFiles) +# sys.stderr.write("%s\n" % removedFiles) + + print "commit refs/heads/master" + if author in users: + print "committer %s 1 2" % users[author] + else: + print "committer %s 1 2" % author + print "data < Date: Wed, 31 Jan 2007 09:39:20 +0100 Subject: Added basic support for specifying the depot path to import from as well as the range of perforce changes. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 238f6e3524..abd6da4668 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -17,10 +17,22 @@ # import os, string, sys -# yep, that's hardcoded right. will fix to a commandline option rsn :) -prefix = "//depot/qt/main/" -# that's in revision range syntax, for example @2342,523634 +if len(sys.argv) != 2: + sys.stderr.write("usage: %s //depot/path[@revRange]\n" % sys.argv[0]); + sys.stderr.write("\n example:\n"); + sys.stderr.write(" %s //depot/my/project -- to import everything\n"); + sys.stderr.write(" %s //depot/my/project@1,6 -- to import only from revision 1 to 6\n"); + sys.stderr.write("\n"); + sys.exit(1) + +prefix = sys.argv[1] changeRange = "" +try: + atIdx = prefix.index("@") + changeRange = prefix[atIdx:] + prefix = prefix[0:atIdx] +except ValueError: + changeRange = "" def describe(change): output = os.popen("p4 describe %s" % change).readlines() -- cgit v1.2.3 From 06bb04454fb91fbc144ed2b3abb3492c923f98f5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 09:49:41 +0100 Subject: Slightly improved help usage output and made specifying the trailing slash for the depot path optional. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index abd6da4668..8df3d73392 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -20,8 +20,10 @@ import os, string, sys if len(sys.argv) != 2: sys.stderr.write("usage: %s //depot/path[@revRange]\n" % sys.argv[0]); sys.stderr.write("\n example:\n"); - sys.stderr.write(" %s //depot/my/project -- to import everything\n"); - sys.stderr.write(" %s //depot/my/project@1,6 -- to import only from revision 1 to 6\n"); + sys.stderr.write(" %s //depot/my/project/ -- to import everything\n"); + sys.stderr.write(" %s //depot/my/project/@1,6 -- to import only from revision 1 to 6\n"); + sys.stderr.write("\n"); + sys.stderr.write(" (a ... is not needed in the path p4 specification, it's added implicitly)\n"); sys.stderr.write("\n"); sys.exit(1) @@ -34,6 +36,9 @@ try: except ValueError: changeRange = "" +if not prefix.endswith("/"): + prefix += "/" + def describe(change): output = os.popen("p4 describe %s" % change).readlines() -- cgit v1.2.3 From 72b2f0ada30adbb2880d551bd92d9519396897dd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 16:39:46 +0100 Subject: Implemented basic support for converting the date of the perforce change to the git format. The timezone isn't correctly set up yet though. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 8df3d73392..689349d551 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -5,8 +5,7 @@ # Author: Simon Hausmann # License: MIT # -# TODO: - fix date parsing (how hard can it be?) -# - support integrations (at least p4i) +# TODO: - support integrations (at least p4i) # - support incremental imports # - create tags # - instead of reading all files into a variable try to pipe from @@ -15,7 +14,7 @@ # - support p4 submit (hah!) # - don't hardcode the import to master # -import os, string, sys +import os, string, sys, time if len(sys.argv) != 2: sys.stderr.write("usage: %s //depot/path[@revRange]\n" % sys.argv[0]); @@ -44,22 +43,25 @@ def describe(change): firstLine = output[0] - author = firstLine.split(" ")[3] + splitted = firstLine.split(" ") + author = splitted[3] author = author[:author.find("@")] + tm = time.strptime(splitted[5] + " " + splitted[6] + time.tzname[0], "%Y/%m/%d %H:%M:%S %Z") + epoch = int(time.mktime(tm)) filesSection = 0 try: filesSection = output.index("Affected files ...\n") except ValueError: sys.stderr.write("Change %s doesn't seem to affect any files. Weird.\n" % change) - return [], [], [], [] + return [], [], [], [], [] differencesSection = 0 try: differencesSection = output.index("Differences ...\n") except ValueError: sys.stderr.write("Change %s doesn't seem to have a differences section. Weird.\n" % change) - return [], [], [], [] + return [], [], [], [], [] log = output[2:filesSection - 1] @@ -85,7 +87,7 @@ def describe(change): else: changed.append(path) - return author, log, changed, removed + return author, log, epoch, changed, removed def p4cat(path): return os.popen("p4 print -q \"%s\"" % path).read() @@ -124,9 +126,9 @@ changes.reverse() sys.stderr.write("\n") -cnt = 0 +cnt = 1 for change in changes: - [ author, log, changedFiles, removedFiles ] = describe(change) + [ author, log, epoch, changedFiles, removedFiles ] = describe(change) sys.stderr.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) cnt = cnt + 1 # sys.stderr.write("%s\n" % log) @@ -135,9 +137,9 @@ for change in changes: print "commit refs/heads/master" if author in users: - print "committer %s 1 2" % users[author] + print "committer %s %s +0000" % (users[author], epoch) else: - print "committer %s 1 2" % author + print "committer %s %s +0000" % (author, epoch) print "data < Date: Wed, 31 Jan 2007 19:43:16 +0100 Subject: Some fixes to the timezone conversion between the date of a perforce change and the git commit. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 689349d551..f3b5f35cb3 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -46,7 +46,7 @@ def describe(change): splitted = firstLine.split(" ") author = splitted[3] author = author[:author.find("@")] - tm = time.strptime(splitted[5] + " " + splitted[6] + time.tzname[0], "%Y/%m/%d %H:%M:%S %Z") + tm = time.strptime(splitted[5] + " " + splitted[6], "%Y/%m/%d %H:%M:%S ") epoch = int(time.mktime(tm)) filesSection = 0 @@ -126,6 +126,8 @@ changes.reverse() sys.stderr.write("\n") +tz = - time.timezone / 36 + cnt = 1 for change in changes: [ author, log, epoch, changedFiles, removedFiles ] = describe(change) @@ -137,9 +139,9 @@ for change in changes: print "commit refs/heads/master" if author in users: - print "committer %s %s +0000" % (users[author], epoch) + print "committer %s %s %s" % (users[author], epoch, tz) else: - print "committer %s %s +0000" % (author, epoch) + print "committer %s %s %s" % (author, epoch, tz) print "data < Date: Wed, 31 Jan 2007 20:16:26 +0100 Subject: Speed up the import of individual files from Perforce into git by passing the output of "p4 print" directly to git fast-import. Also try to set the mode of the file in git correctly based on file type heuristics. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index f3b5f35cb3..72a4fd70a5 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -5,12 +5,11 @@ # Author: Simon Hausmann # License: MIT # -# TODO: - support integrations (at least p4i) +# TODO: +# - support integrations (at least p4i) # - support incremental imports # - create tags # - instead of reading all files into a variable try to pipe from -# - p4 print directly to stdout. need to figure out file size somehow -# though. # - support p4 submit (hah!) # - don't hardcode the import to master # @@ -92,6 +91,17 @@ def describe(change): def p4cat(path): return os.popen("p4 print -q \"%s\"" % path).read() +def p4Stat(path): + output = os.popen("p4 fstat -Ol \"%s\"" % path).readlines() + fileSize = 0 + mode = 644 + for line in output: + if line.startswith("... headType x"): + mode = 755 + elif line.startswith("... fileSize "): + fileSize = long(line[12:]) + return mode, fileSize + def stripRevision(path): hashPos = path.rindex("#") return path[:hashPos] @@ -112,7 +122,6 @@ def getUserMap(): return users - users = getUserMap() output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() @@ -133,9 +142,6 @@ for change in changes: [ author, log, epoch, changedFiles, removedFiles ] = describe(change) sys.stderr.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) cnt = cnt + 1 -# sys.stderr.write("%s\n" % log) -# sys.stderr.write("%s\n" % changedFiles) -# sys.stderr.write("%s\n" % removedFiles) print "commit refs/heads/master" if author in users: @@ -154,10 +160,13 @@ for change in changes: sys.stderr.write("\nchanged files: ignoring path %s outside of %s in change %s\n" % (f, prefix, change)) continue relpath = f[len(prefix):] - print "M 644 inline %s" % stripRevision(relpath) - data = p4cat(f) - print "data %s" % len(data) - sys.stdout.write(data) + + [mode, fileSize] = p4Stat(f) + + print "M %s inline %s" % (mode, stripRevision(relpath)) + print "data %s" % fileSize + sys.stdout.flush(); + os.system("p4 print -q \"%s\"" % f) print "" for f in removedFiles: -- cgit v1.2.3 From 3f2ddd47c7f311516dc7092b0a89b46291e40271 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 20:48:39 +0100 Subject: Removed unused p4cat function and added helper function for the perforce python interface (p4Cmd). Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 72a4fd70a5..e284b19502 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -14,6 +14,7 @@ # - don't hardcode the import to master # import os, string, sys, time +import marshal if len(sys.argv) != 2: sys.stderr.write("usage: %s //depot/path[@revRange]\n" % sys.argv[0]); @@ -37,6 +38,18 @@ except ValueError: if not prefix.endswith("/"): prefix += "/" +def p4Cmd(cmd): + pipe = os.popen("p4 -G %s" % cmd, "rb") + result = {} + try: + while True: + entry = marshal.load(pipe) + result.update(entry) + except EOFError: + pass + pipe.close() + return result + def describe(change): output = os.popen("p4 describe %s" % change).readlines() @@ -88,9 +101,6 @@ def describe(change): return author, log, epoch, changed, removed -def p4cat(path): - return os.popen("p4 print -q \"%s\"" % path).read() - def p4Stat(path): output = os.popen("p4 fstat -Ol \"%s\"" % path).readlines() fileSize = 0 -- cgit v1.2.3 From 701ce876331eca10031c104f42cc6fccc425ac94 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 21:54:56 +0100 Subject: Changed the import mechanism to write to git fast-import through a pipe instead of having p4-fast-export write to stdout and let the caller connect it to git fast-import. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 52 ++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 25 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index e284b19502..662dd01d8d 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -14,16 +14,16 @@ # - don't hardcode the import to master # import os, string, sys, time -import marshal +import marshal, popen2 if len(sys.argv) != 2: - sys.stderr.write("usage: %s //depot/path[@revRange]\n" % sys.argv[0]); - sys.stderr.write("\n example:\n"); - sys.stderr.write(" %s //depot/my/project/ -- to import everything\n"); - sys.stderr.write(" %s //depot/my/project/@1,6 -- to import only from revision 1 to 6\n"); - sys.stderr.write("\n"); - sys.stderr.write(" (a ... is not needed in the path p4 specification, it's added implicitly)\n"); - sys.stderr.write("\n"); + print "usage: %s //depot/path[@revRange]" % sys.argv[0] + print "\n example:" + print " %s //depot/my/project/ -- to import everything" + print " %s //depot/my/project/@1,6 -- to import only from revision 1 to 6" + print "" + print " (a ... is not needed in the path p4 specification, it's added implicitly)" + print "" sys.exit(1) prefix = sys.argv[1] @@ -147,23 +147,23 @@ sys.stderr.write("\n") tz = - time.timezone / 36 +gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") + cnt = 1 for change in changes: [ author, log, epoch, changedFiles, removedFiles ] = describe(change) - sys.stderr.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) cnt = cnt + 1 - print "commit refs/heads/master" + gitStream.write("commit refs/heads/master\n") if author in users: - print "committer %s %s %s" % (users[author], epoch, tz) + gitStream.write("committer %s %s %s\n" % (users[author], epoch, tz)) else: - print "committer %s %s %s" % (author, epoch, tz) - print "data < %s %s\n" % (author, epoch, tz)) + gitStream.write("data < Date: Wed, 31 Jan 2007 22:13:17 +0100 Subject: Minor code cleanups and ported some p4 interfacing code over to the p4 python mode. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 53 +++++++++-------------------------- 1 file changed, 14 insertions(+), 39 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 662dd01d8d..3ccef526eb 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -51,54 +51,30 @@ def p4Cmd(cmd): return result def describe(change): - output = os.popen("p4 describe %s" % change).readlines() + describeOutput = p4Cmd("describe %s" % change) - firstLine = output[0] + author = describeOutput["user"] + epoch = describeOutput["time"] - splitted = firstLine.split(" ") - author = splitted[3] - author = author[:author.find("@")] - tm = time.strptime(splitted[5] + " " + splitted[6], "%Y/%m/%d %H:%M:%S ") - epoch = int(time.mktime(tm)) - - filesSection = 0 - try: - filesSection = output.index("Affected files ...\n") - except ValueError: - sys.stderr.write("Change %s doesn't seem to affect any files. Weird.\n" % change) - return [], [], [], [], [] - - differencesSection = 0 - try: - differencesSection = output.index("Differences ...\n") - except ValueError: - sys.stderr.write("Change %s doesn't seem to have a differences section. Weird.\n" % change) - return [], [], [], [], [] - - log = output[2:filesSection - 1] - - lines = output[filesSection + 2:differencesSection - 1] + log = describeOutput["desc"] changed = [] removed = [] - for line in lines: - # chop off "... " and trailing newline - line = line[4:len(line) - 1] - - lastSpace = line.rfind(" ") - if lastSpace == -1: - sys.stderr.write("trouble parsing line %s, skipping!\n" % line) - continue + i = 0 + while describeOutput.has_key("depotFile%s" % i): + path = describeOutput["depotFile%s" % i] + rev = describeOutput["rev%s" % i] + action = describeOutput["action%s" % i] + path = path + "#" + rev - operation = line[lastSpace + 1:] - path = line[:lastSpace] - - if operation == "delete": + if action == "delete": removed.append(path) else: changed.append(path) + i = i + 1 + return author, log, epoch, changed, removed def p4Stat(path): @@ -161,8 +137,7 @@ for change in changes: else: gitStream.write("committer %s %s %s\n" % (author, epoch, tz)) gitStream.write("data < Date: Wed, 31 Jan 2007 22:19:18 +0100 Subject: Instead of parsing the output of "p4 users" use the python objects of "p4 -G users". Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 3ccef526eb..d3e65f0f39 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -38,18 +38,25 @@ except ValueError: if not prefix.endswith("/"): prefix += "/" -def p4Cmd(cmd): +def p4CmdList(cmd): pipe = os.popen("p4 -G %s" % cmd, "rb") - result = {} + result = [] try: while True: entry = marshal.load(pipe) - result.update(entry) + result.append(entry) except EOFError: pass pipe.close() return result +def p4Cmd(cmd): + list = p4CmdList(cmd) + result = {} + for entry in list: + result.update(entry) + return result; + def describe(change): describeOutput = p4Cmd("describe %s" % change) @@ -94,18 +101,11 @@ def stripRevision(path): def getUserMap(): users = {} - output = os.popen("p4 users") - for line in output: - firstSpace = line.index(" ") - secondSpace = line.index(" ", firstSpace + 1) - key = line[:firstSpace] - email = line[firstSpace + 1:secondSpace] - openParenPos = line.index("(", secondSpace) - closedParenPos = line.index(")", openParenPos) - name = line[openParenPos + 1:closedParenPos] - - users[key] = name + " " + email + for output in p4CmdList("users"): + if not output.has_key("User"): + continue + users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" return users users = getUserMap() -- cgit v1.2.3 From 0dd0b9d01116d57c612a5fa90de803bdd836ce11 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 22:31:28 +0100 Subject: Ported the remaining functions that parsed p4 shell output over to the p4 python interface. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 89 ++++++++++++----------------------- 1 file changed, 31 insertions(+), 58 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index d3e65f0f39..45d5157961 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -57,47 +57,8 @@ def p4Cmd(cmd): result.update(entry) return result; -def describe(change): - describeOutput = p4Cmd("describe %s" % change) - - author = describeOutput["user"] - epoch = describeOutput["time"] - - log = describeOutput["desc"] - - changed = [] - removed = [] - - i = 0 - while describeOutput.has_key("depotFile%s" % i): - path = describeOutput["depotFile%s" % i] - rev = describeOutput["rev%s" % i] - action = describeOutput["action%s" % i] - path = path + "#" + rev - - if action == "delete": - removed.append(path) - else: - changed.append(path) - - i = i + 1 - - return author, log, epoch, changed, removed - -def p4Stat(path): - output = os.popen("p4 fstat -Ol \"%s\"" % path).readlines() - fileSize = 0 - mode = 644 - for line in output: - if line.startswith("... headType x"): - mode = 755 - elif line.startswith("... fileSize "): - fileSize = long(line[12:]) - return mode, fileSize - -def stripRevision(path): - hashPos = path.rindex("#") - return path[:hashPos] +def p4FileSize(path): + return int(p4Cmd("fstat -Ol \"%s\"" % path)["fileSize"]) def getUserMap(): users = {} @@ -127,38 +88,50 @@ gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") cnt = 1 for change in changes: - [ author, log, epoch, changedFiles, removedFiles ] = describe(change) + description = p4Cmd("describe %s" % change) + sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) cnt = cnt + 1 + epoch = description["time"] + author = description["user"] + gitStream.write("commit refs/heads/master\n") if author in users: gitStream.write("committer %s %s %s\n" % (users[author], epoch, tz)) else: gitStream.write("committer %s %s %s\n" % (author, epoch, tz)) gitStream.write("data < Date: Wed, 31 Jan 2007 22:38:07 +0100 Subject: Avoid calling fstat for every imported file (slow!) and instead read the file data first into the python process and use the length of the bytes read for the size field of git fast-import. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 45d5157961..133447c4e7 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -57,9 +57,6 @@ def p4Cmd(cmd): result.update(entry) return result; -def p4FileSize(path): - return int(p4Cmd("fstat -Ol \"%s\"" % path)["fileSize"]) - def getUserMap(): users = {} @@ -121,14 +118,15 @@ for change in changes: if action == "delete": gitStream.write("D %s\n" % relPath) else: - fileSize = p4FileSize(depotPath) mode = 644 if description["type%s" % fnum].startswith("x"): mode = 755 + data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + gitStream.write("M %s inline %s\n" % (mode, relPath)) - gitStream.write("data %s\n" % fileSize) - gitStream.write(os.popen("p4 print -q \"%s\"" % depotPath).read()) + gitStream.write("data %s\n" % len(data)) + gitStream.write(data) gitStream.write("\n") fnum = fnum + 1 -- cgit v1.2.3 From f26037dce365b3e525596c6f0236ab203d87a872 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 22:41:08 +0100 Subject: Permit calling p4-fast-export with a depot path that has the typical ... wildcard at the end. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 133447c4e7..72e01224bf 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -35,6 +35,9 @@ try: except ValueError: changeRange = "" +if prefix.endswith("..."): + prefix = prefix[:-3] + if not prefix.endswith("/"): prefix += "/" -- cgit v1.2.3 From 214bed82390479bdba31337ac420bb09e9b366e9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 22:47:53 +0100 Subject: Fixed displaying import progress by calling flush on stdout. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 72e01224bf..a1dc54013e 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -91,6 +91,7 @@ for change in changes: description = p4Cmd("describe %s" % change) sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() cnt = cnt + 1 epoch = description["time"] -- cgit v1.2.3 From 71f7c0d0bb20936fc831e49bbfc9355f1e5ca211 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 31 Jan 2007 23:03:01 +0100 Subject: Create a git tag for every changeset imported from perforce. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index a1dc54013e..588554d672 100644 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -98,12 +98,17 @@ for change in changes: author = description["user"] gitStream.write("commit refs/heads/master\n") + committer = "" if author in users: - gitStream.write("committer %s %s %s\n" % (users[author], epoch, tz)) + committer = "%s %s %s" % (users[author], epoch, tz) else: - gitStream.write("committer %s %s %s\n" % (author, epoch, tz)) + committer = "%s %s %s" % (author, epoch, tz) + + gitStream.write("committer %s\n" % committer) + gitStream.write("data < Date: Wed, 31 Jan 2007 23:09:24 +0100 Subject: Fix file permissions of p4-fast-export.py to be executable. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 contrib/fast-import/p4-fast-export.py (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py old mode 100644 new mode 100755 -- cgit v1.2.3 From 61b3cf7c471d2f94eefcf6616c1204f32e641542 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 1 Feb 2007 00:08:51 +0100 Subject: Started working on incremental imports from Perforce. Try to find the last imported p4 change number from the git tags and try to pass the right parent for commits to git fast-import. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 37 +++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 588554d672..da3eb35841 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -26,6 +26,8 @@ if len(sys.argv) != 2: print "" sys.exit(1) +master = "refs/heads/p4" +branch = "refs/heads/p4-import" prefix = sys.argv[1] changeRange = "" try: @@ -70,6 +72,25 @@ def getUserMap(): return users users = getUserMap() +topMerge = "" + +incremental = 0 +# try incremental import +if len(changeRange) == 0: + try: + sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % master) + output = sout.read() + tagIdx = output.index(" tags/p4/") + caretIdx = output.index("^") + revision = int(output[tagIdx + 9 : caretIdx]) + 1 + changeRange = "@%s,#head" % revision + topMerge = os.popen("git-rev-parse %s" % master).read()[:-1] + incremental = 1 + except: + pass + +if incremental == 0: + branch = master output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() @@ -80,6 +101,10 @@ for line in output: changes.reverse() +if len(changes) == 0: + print "no changes to import!" + sys.exit(1) + sys.stderr.write("\n") tz = - time.timezone / 36 @@ -97,7 +122,7 @@ for change in changes: epoch = description["time"] author = description["user"] - gitStream.write("commit refs/heads/master\n") + gitStream.write("commit %s\n" % branch) committer = "" if author in users: committer = "%s %s %s" % (users[author], epoch, tz) @@ -111,6 +136,10 @@ for change in changes: gitStream.write("\n[ imported from %s; change %s ]\n" % (prefix, change)) gitStream.write("EOT\n\n") + if len(topMerge) > 0: + gitStream.write("merge %s\n" % topMerge) + topMerge = "" + fnum = 0 while description.has_key("depotFile%s" % fnum): path = description["depotFile%s" % fnum] @@ -143,7 +172,7 @@ for change in changes: gitStream.write("\n") gitStream.write("tag p4/%s\n" % change) - gitStream.write("from refs/heads/master\n"); + gitStream.write("from %s\n" % branch); gitStream.write("tagger %s\n" % committer); gitStream.write("data 0\n\n") @@ -152,4 +181,8 @@ gitStream.close() gitOutput.close() gitError.close() +if incremental == 1: + os.popen("git rebase p4-import p4") + os.popen("git branch -d p4-import") + print "" -- cgit v1.2.3 From f16255f559c96b8c5fb096b00c94a69f5fcc498f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 1 Feb 2007 08:23:39 +0100 Subject: Simplify the incremental import by elimination the need for a temporary import branch. It turns out that git fast-import can "resume" from an existing branch just fine. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index da3eb35841..c5b15206b1 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -7,11 +7,7 @@ # # TODO: # - support integrations (at least p4i) -# - support incremental imports -# - create tags -# - instead of reading all files into a variable try to pipe from # - support p4 submit (hah!) -# - don't hardcode the import to master # import os, string, sys, time import marshal, popen2 @@ -26,8 +22,7 @@ if len(sys.argv) != 2: print "" sys.exit(1) -master = "refs/heads/p4" -branch = "refs/heads/p4-import" +branch = "refs/heads/p4" prefix = sys.argv[1] changeRange = "" try: @@ -74,24 +69,18 @@ def getUserMap(): users = getUserMap() topMerge = "" -incremental = 0 -# try incremental import if len(changeRange) == 0: try: - sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % master) + sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) output = sout.read() tagIdx = output.index(" tags/p4/") caretIdx = output.index("^") revision = int(output[tagIdx + 9 : caretIdx]) + 1 changeRange = "@%s,#head" % revision - topMerge = os.popen("git-rev-parse %s" % master).read()[:-1] - incremental = 1 + topMerge = os.popen("git-rev-parse %s" % branch).read()[:-1] except: pass -if incremental == 0: - branch = master - output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() changes = [] @@ -181,8 +170,4 @@ gitStream.close() gitOutput.close() gitError.close() -if incremental == 1: - os.popen("git rebase p4-import p4") - os.popen("git branch -d p4-import") - print "" -- cgit v1.2.3 From 68f1336fe370ca2153d691631876b0f85a3fb17f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 1 Feb 2007 17:42:23 +0100 Subject: Code cleanups, move the code to create a commit with fast-import into a separate function out of the main loop. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 126 ++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 59 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index c5b15206b1..8455c1f41c 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -25,6 +25,9 @@ if len(sys.argv) != 2: branch = "refs/heads/p4" prefix = sys.argv[1] changeRange = "" +users = {} +initialParent = "" + try: atIdx = prefix.index("@") changeRange = prefix[atIdx:] @@ -57,6 +60,68 @@ def p4Cmd(cmd): result.update(entry) return result; +def commit(details): + global initialParent + global users + + epoch = details["time"] + author = details["user"] + + gitStream.write("commit %s\n" % branch) + committer = "" + if author in users: + committer = "%s %s %s" % (users[author], epoch, tz) + else: + committer = "%s %s %s" % (author, epoch, tz) + + gitStream.write("committer %s\n" % committer) + + gitStream.write("data < 0: + gitStream.write("merge %s\n" % initialParent) + initialParent = "" + + fnum = 0 + while details.has_key("depotFile%s" % fnum): + path = details["depotFile%s" % fnum] + if not path.startswith(prefix): + print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change) + fnum = fnum + 1 + continue + + rev = details["rev%s" % fnum] + depotPath = path + "#" + rev + relPath = path[len(prefix):] + action = details["action%s" % fnum] + + if action == "delete": + gitStream.write("D %s\n" % relPath) + else: + mode = 644 + if details["type%s" % fnum].startswith("x"): + mode = 755 + + data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + + gitStream.write("M %s inline %s\n" % (mode, relPath)) + gitStream.write("data %s\n" % len(data)) + gitStream.write(data) + gitStream.write("\n") + + fnum = fnum + 1 + + gitStream.write("\n") + + gitStream.write("tag p4/%s\n" % change) + gitStream.write("from %s\n" % branch); + gitStream.write("tagger %s\n" % committer); + gitStream.write("data 0\n\n") + + def getUserMap(): users = {} @@ -67,7 +132,6 @@ def getUserMap(): return users users = getUserMap() -topMerge = "" if len(changeRange) == 0: try: @@ -77,7 +141,7 @@ if len(changeRange) == 0: caretIdx = output.index("^") revision = int(output[tagIdx + 9 : caretIdx]) + 1 changeRange = "@%s,#head" % revision - topMerge = os.popen("git-rev-parse %s" % branch).read()[:-1] + initialParent = os.popen("git-rev-parse %s" % branch).read()[:-1] except: pass @@ -108,63 +172,7 @@ for change in changes: sys.stdout.flush() cnt = cnt + 1 - epoch = description["time"] - author = description["user"] - - gitStream.write("commit %s\n" % branch) - committer = "" - if author in users: - committer = "%s %s %s" % (users[author], epoch, tz) - else: - committer = "%s %s %s" % (author, epoch, tz) - - gitStream.write("committer %s\n" % committer) - - gitStream.write("data < 0: - gitStream.write("merge %s\n" % topMerge) - topMerge = "" - - fnum = 0 - while description.has_key("depotFile%s" % fnum): - path = description["depotFile%s" % fnum] - if not path.startswith(prefix): - print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change) - fnum = fnum + 1 - continue - - rev = description["rev%s" % fnum] - depotPath = path + "#" + rev - relPath = path[len(prefix):] - action = description["action%s" % fnum] - - if action == "delete": - gitStream.write("D %s\n" % relPath) - else: - mode = 644 - if description["type%s" % fnum].startswith("x"): - mode = 755 - - data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() - - gitStream.write("M %s inline %s\n" % (mode, relPath)) - gitStream.write("data %s\n" % len(data)) - gitStream.write(data) - gitStream.write("\n") - - fnum = fnum + 1 - - gitStream.write("\n") - - gitStream.write("tag p4/%s\n" % change) - gitStream.write("from %s\n" % branch); - gitStream.write("tagger %s\n" % committer); - gitStream.write("data 0\n\n") - + commit(description) gitStream.close() gitOutput.close() -- cgit v1.2.3 From 6d48d12f5d34a2acb6d8b56bfe3c26d4db92b1a5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 1 Feb 2007 18:19:55 +0100 Subject: Initial support for importing a directory from Perforce at a specified revision. Use p4 files //depot/path/...@revision to determine the state of the project and create a "fake" git commit from it. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 94 ++++++++++++++++++++++++----------- 1 file changed, 65 insertions(+), 29 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 8455c1f41c..19f5f03476 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -25,15 +25,21 @@ if len(sys.argv) != 2: branch = "refs/heads/p4" prefix = sys.argv[1] changeRange = "" +revision = "" users = {} initialParent = "" -try: +if prefix.find("@") != -1: atIdx = prefix.index("@") changeRange = prefix[atIdx:] + if changeRange.find(",") == -1: + revision = changeRange + changeRange = "" prefix = prefix[0:atIdx] -except ValueError: - changeRange = "" +elif prefix.find("#") != -1: + hashIdx = prefix.index("#") + revision = prefix[hashIdx:] + prefix = prefix[0:hashIdx] if prefix.endswith("..."): prefix = prefix[:-3] @@ -78,7 +84,7 @@ def commit(details): gitStream.write("data < 0: @@ -116,7 +122,7 @@ def commit(details): gitStream.write("\n") - gitStream.write("tag p4/%s\n" % change) + gitStream.write("tag p4/%s\n" % details["change"]) gitStream.write("from %s\n" % branch); gitStream.write("tagger %s\n" % committer); gitStream.write("data 0\n\n") @@ -139,43 +145,73 @@ if len(changeRange) == 0: output = sout.read() tagIdx = output.index(" tags/p4/") caretIdx = output.index("^") - revision = int(output[tagIdx + 9 : caretIdx]) + 1 - changeRange = "@%s,#head" % revision + rev = int(output[tagIdx + 9 : caretIdx]) + 1 + changeRange = "@%s,#head" % rev initialParent = os.popen("git-rev-parse %s" % branch).read()[:-1] except: pass -output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() +sys.stderr.write("\n") -changes = [] -for line in output: - changeNum = line.split(" ")[1] - changes.append(changeNum) +tz = - time.timezone / 36 -changes.reverse() +if len(revision) > 0: + print "Doing initial import of %s from revision %s" % (prefix, revision) -if len(changes) == 0: - print "no changes to import!" - sys.exit(1) + details = { "user" : "git perforce import user", "time" : int(time.time()) } + details["desc"] = "Initial import of %s from the state at revision %s" % (prefix, revision) + details["change"] = revision + newestRevision = 0 -sys.stderr.write("\n") + fileCnt = 0 + for info in p4CmdList("files %s...%s" % (prefix, revision)): + if info["action"] == "delete": + continue + for prop in [ "depotFile", "rev", "action", "type" ]: + details["%s%s" % (prop, fileCnt)] = info[prop] -tz = - time.timezone / 36 + change = info["change"] + if change > newestRevision: + newestRevision = change + + fileCnt = fileCnt + 1 + + details["change"] = newestRevision + + gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") + commit(details) + + gitStream.close() + gitOutput.close() + gitError.close() +else: + output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() + + changes = [] + for line in output: + changeNum = line.split(" ")[1] + changes.append(changeNum) + + changes.reverse() + + if len(changes) == 0: + print "no changes to import!" + sys.exit(1) -gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") + gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") -cnt = 1 -for change in changes: - description = p4Cmd("describe %s" % change) + cnt = 1 + for change in changes: + description = p4Cmd("describe %s" % change) - sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) - sys.stdout.flush() - cnt = cnt + 1 + sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() + cnt = cnt + 1 - commit(description) + commit(description) -gitStream.close() -gitOutput.close() -gitError.close() + gitStream.close() + gitOutput.close() + gitError.close() print "" -- cgit v1.2.3 From c4cf2d4f8793ea8f889052391d72dd4066e88155 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Feb 2007 22:57:01 +0100 Subject: Minor cleanups and print an error message of git fast-import if it fails. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 19f5f03476..06de0eae6d 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -154,6 +154,9 @@ if len(changeRange) == 0: sys.stderr.write("\n") tz = - time.timezone / 36 +tzsign = ("%s" % tz)[0] +if tzsign != '+' and tzsign != '-': + tz = "+" + ("%s" % tz) if len(revision) > 0: print "Doing initial import of %s from revision %s" % (prefix, revision) @@ -165,21 +168,25 @@ if len(revision) > 0: fileCnt = 0 for info in p4CmdList("files %s...%s" % (prefix, revision)): + change = info["change"] + if change > newestRevision: + newestRevision = change + if info["action"] == "delete": continue + for prop in [ "depotFile", "rev", "action", "type" ]: details["%s%s" % (prop, fileCnt)] = info[prop] - change = info["change"] - if change > newestRevision: - newestRevision = change - fileCnt = fileCnt + 1 details["change"] = newestRevision gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") - commit(details) + try: + commit(details) + except: + print gitError.read() gitStream.close() gitOutput.close() -- cgit v1.2.3 From e3d37cf0980646b3dbc19e01684c665482a3129d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Feb 2007 23:09:49 +0100 Subject: Fixed incremental imports by using the correct "from" command instead of "merge" with git fast-import. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 06de0eae6d..3e573cd786 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -88,7 +88,7 @@ def commit(details): gitStream.write("EOT\n\n") if len(initialParent) > 0: - gitStream.write("merge %s\n" % initialParent) + gitStream.write("from %s\n" % initialParent) initialParent = "" fnum = 0 -- cgit v1.2.3 From 1cd573866a76d62866b65c458e723983ba58117e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Feb 2007 23:25:56 +0100 Subject: Make incremental imports easier to use by storing the p4 depot path after an import in .git/config and re-using it when we're invoked again later. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 3e573cd786..be4fd36c36 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -12,7 +12,14 @@ import os, string, sys, time import marshal, popen2 -if len(sys.argv) != 2: +branch = "refs/heads/p4" +prefix = os.popen("git-repo-config --get p4.depotpath").read() +if len(prefix) != 0: + prefix = prefix[:-1] + +if len(sys.argv) == 1 and len(prefix) != 0: + print "[using previously specified depot path %s]" % prefix +elif len(sys.argv) != 2: print "usage: %s //depot/path[@revRange]" % sys.argv[0] print "\n example:" print " %s //depot/my/project/ -- to import everything" @@ -21,9 +28,12 @@ if len(sys.argv) != 2: print " (a ... is not needed in the path p4 specification, it's added implicitly)" print "" sys.exit(1) +else: + if len(prefix) != 0 and prefix != sys.argv[1]: + print "previous import used depot path %s and now %s was specified. this doesn't work!" % (prefix, sys.argv[1]) + sys.exit(1) + prefix = sys.argv[1] -branch = "refs/heads/p4" -prefix = sys.argv[1] changeRange = "" revision = "" users = {} @@ -222,3 +232,6 @@ else: gitError.close() print "" + +os.popen("git-repo-config p4.depotpath %s" % prefix).read() + -- cgit v1.2.3 From 23efd2545b292208740b0f2fb7446d99ce744000 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Feb 2007 23:37:54 +0100 Subject: Make specifying the revision ranges more convenient. Added support for @all as revision range specifier to import all changes to a given depot path. Also default to an import of #head if no revrange is specified. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index be4fd36c36..16e3d8d42a 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -13,7 +13,7 @@ import os, string, sys, time import marshal, popen2 branch = "refs/heads/p4" -prefix = os.popen("git-repo-config --get p4.depotpath").read() +prefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() if len(prefix) != 0: prefix = prefix[:-1] @@ -22,7 +22,8 @@ if len(sys.argv) == 1 and len(prefix) != 0: elif len(sys.argv) != 2: print "usage: %s //depot/path[@revRange]" % sys.argv[0] print "\n example:" - print " %s //depot/my/project/ -- to import everything" + print " %s //depot/my/project/ -- to import the current head" + print " %s //depot/my/project/@all -- to import everything" print " %s //depot/my/project/@1,6 -- to import only from revision 1 to 6" print "" print " (a ... is not needed in the path p4 specification, it's added implicitly)" @@ -42,7 +43,9 @@ initialParent = "" if prefix.find("@") != -1: atIdx = prefix.index("@") changeRange = prefix[atIdx:] - if changeRange.find(",") == -1: + if changeRange == "@all": + changeRange = "" + elif changeRange.find(",") == -1: revision = changeRange changeRange = "" prefix = prefix[0:atIdx] @@ -50,6 +53,8 @@ elif prefix.find("#") != -1: hashIdx = prefix.index("#") revision = prefix[hashIdx:] prefix = prefix[0:hashIdx] +elif len(previousDepotPath) == 0: + revision = "#head" if prefix.endswith("..."): prefix = prefix[:-3] -- cgit v1.2.3 From 1e30c07dfcd0ca74ff4aec2004b5722e69b5a639 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Feb 2007 23:51:51 +0100 Subject: Fix calculation of the newest imported revision for #head imports. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 16e3d8d42a..36381fbecd 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -183,7 +183,7 @@ if len(revision) > 0: fileCnt = 0 for info in p4CmdList("files %s...%s" % (prefix, revision)): - change = info["change"] + change = int(info["change"]) if change > newestRevision: newestRevision = change -- cgit v1.2.3 From 7315866824607e0a7da807dbee84c60cfe5c5719 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 8 Feb 2007 15:45:16 +0100 Subject: Catch io exceptions from git fast-import again and print the error message. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 36381fbecd..9f4b16a974 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -230,7 +230,11 @@ else: sys.stdout.flush() cnt = cnt + 1 - commit(description) + try: + commit(description) + except: + print gitError.read() + sys.exit(1) gitStream.close() gitOutput.close() @@ -240,3 +244,4 @@ print "" os.popen("git-repo-config p4.depotpath %s" % prefix).read() +sys.exit(0) -- cgit v1.2.3 From c9c527d7b60f406e143c1f7947a8c6c5ea9d9a8a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 8 Feb 2007 15:53:11 +0100 Subject: Made the name of the git branch used for the perforce import configurable through a new --branch= commandline option. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 9f4b16a974..c44292473a 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -10,16 +10,26 @@ # - support p4 submit (hah!) # import os, string, sys, time -import marshal, popen2 +import marshal, popen2, getopt branch = "refs/heads/p4" prefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() if len(prefix) != 0: prefix = prefix[:-1] -if len(sys.argv) == 1 and len(prefix) != 0: +try: + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=" ]) +except getopt.GetoptError: + print "fixme, syntax error" + sys.exit(1) + +for o, a in opts: + if o == "--branch": + branch = "refs/heads/" + a + +if len(args) == 0 and len(prefix) != 0: print "[using previously specified depot path %s]" % prefix -elif len(sys.argv) != 2: +elif len(args) != 1: print "usage: %s //depot/path[@revRange]" % sys.argv[0] print "\n example:" print " %s //depot/my/project/ -- to import the current head" @@ -30,10 +40,10 @@ elif len(sys.argv) != 2: print "" sys.exit(1) else: - if len(prefix) != 0 and prefix != sys.argv[1]: - print "previous import used depot path %s and now %s was specified. this doesn't work!" % (prefix, sys.argv[1]) + if len(prefix) != 0 and prefix != args[0]: + print "previous import used depot path %s and now %s was specified. this doesn't work!" % (prefix, args[0]) sys.exit(1) - prefix = sys.argv[1] + prefix = args[0] changeRange = "" revision = "" -- cgit v1.2.3 From 20c7bc76b90084da6b99a1627427c000c06ef53a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 8 Feb 2007 23:00:19 +0100 Subject: Added a little helper script to debug the output of the p4 python interface. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-debug.p4 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100755 contrib/fast-import/p4-debug.p4 (limited to 'contrib') diff --git a/contrib/fast-import/p4-debug.p4 b/contrib/fast-import/p4-debug.p4 new file mode 100755 index 0000000000..8fb159fd6a --- /dev/null +++ b/contrib/fast-import/p4-debug.p4 @@ -0,0 +1,25 @@ +#!/usr/bin/python +# +# p4-debug.py +# +# Author: Simon Hausmann +# License: MIT +# +# executes a p4 command with -G and prints the resulting python dicts +# +import os, string, sys +import marshal, popen2 + +cmd = "" +for arg in sys.argv[1:]: + cmd += arg + " " + +pipe = os.popen("p4 -G %s" % cmd, "rb") +try: + while True: + entry = marshal.load(pipe) + print entry +except EOFError: + pass +pipe.close() + -- cgit v1.2.3 From b41507a42769c9e40d024593c6924769d9cb961b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Feb 2007 09:25:22 +0100 Subject: Minor code cleanups. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index c44292473a..d1faa7c290 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -183,6 +183,8 @@ tzsign = ("%s" % tz)[0] if tzsign != '+' and tzsign != '-': tz = "+" + ("%s" % tz) +gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") + if len(revision) > 0: print "Doing initial import of %s from revision %s" % (prefix, revision) @@ -207,15 +209,11 @@ if len(revision) > 0: details["change"] = newestRevision - gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") try: commit(details) except: print gitError.read() - gitStream.close() - gitOutput.close() - gitError.close() else: output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() @@ -230,8 +228,6 @@ else: print "no changes to import!" sys.exit(1) - gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") - cnt = 1 for change in changes: description = p4Cmd("describe %s" % change) @@ -246,12 +242,12 @@ else: print gitError.read() sys.exit(1) - gitStream.close() - gitOutput.close() - gitError.close() - print "" +gitStream.close() +gitOutput.close() +gitError.close() + os.popen("git-repo-config p4.depotpath %s" % prefix).read() sys.exit(0) -- cgit v1.2.3 From 8718f3ec9a550c8c2419576a3765c20189029c99 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Feb 2007 10:05:29 +0100 Subject: Avoid the excessive use of git tags for every perforce change and instead just create one git tag for the last imported change. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index d1faa7c290..907a56dc8a 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -49,6 +49,9 @@ changeRange = "" revision = "" users = {} initialParent = "" +lastChange = "" +lastCommitter = "" +initialTag = "" if prefix.find("@") != -1: atIdx = prefix.index("@") @@ -94,6 +97,8 @@ def p4Cmd(cmd): def commit(details): global initialParent global users + global lastChange + global lastCommitter epoch = details["time"] author = details["user"] @@ -147,11 +152,8 @@ def commit(details): gitStream.write("\n") - gitStream.write("tag p4/%s\n" % details["change"]) - gitStream.write("from %s\n" % branch); - gitStream.write("tagger %s\n" % committer); - gitStream.write("data 0\n\n") - + lastChange = details["change"] + lastCommitter = committer def getUserMap(): users = {} @@ -173,6 +175,7 @@ if len(changeRange) == 0: rev = int(output[tagIdx + 9 : caretIdx]) + 1 changeRange = "@%s,#head" % rev initialParent = os.popen("git-rev-parse %s" % branch).read()[:-1] + initialTag = "p4/%s" % (int(rev) - 1) except: pass @@ -244,10 +247,17 @@ else: print "" +gitStream.write("tag p4/%s\n" % lastChange) +gitStream.write("from %s\n" % branch); +gitStream.write("tagger %s\n" % lastCommitter); +gitStream.write("data 0\n\n") + gitStream.close() gitOutput.close() gitError.close() os.popen("git-repo-config p4.depotpath %s" % prefix).read() +if len(initialTag) > 0: + os.popen("git tag -d %s" % initialTag).read() sys.exit(0) -- cgit v1.2.3 From fe2193183add84d185c0691166fde4460a333412 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Feb 2007 10:05:51 +0100 Subject: Changed the default git import branch from "p4" to "master". Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 907a56dc8a..1f19cbc560 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -12,7 +12,7 @@ import os, string, sys, time import marshal, popen2, getopt -branch = "refs/heads/p4" +branch = "refs/heads/master" prefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() if len(prefix) != 0: prefix = prefix[:-1] -- cgit v1.2.3 From f7d63b0c99945f4b358df06e4d09837f66db6a88 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Feb 2007 10:26:03 +0100 Subject: Added a little helper script to remove unused tags from the perforce import. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-clean-tags.py | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100755 contrib/fast-import/p4-clean-tags.py (limited to 'contrib') diff --git a/contrib/fast-import/p4-clean-tags.py b/contrib/fast-import/p4-clean-tags.py new file mode 100755 index 0000000000..0be51f6405 --- /dev/null +++ b/contrib/fast-import/p4-clean-tags.py @@ -0,0 +1,40 @@ +#!/usr/bin/python +# +# p4-debug.py +# +# Author: Simon Hausmann +# License: MIT +# +# removes unused p4 import tags +# +import os, string, sys +import popen2, getopt + +branch = "refs/heads/master" + +try: + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=" ]) +except getopt.GetoptError: + print "fixme, syntax error" + sys.exit(1) + +for o, a in opts: + if o == "--branch": + branch = "refs/heads/" + a + +sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) +output = sout.read() +tagIdx = output.index(" tags/p4/") +caretIdx = output.index("^") +rev = int(output[tagIdx + 9 : caretIdx]) + +allTags = os.popen("git tag -l p4/").readlines() +for i in range(len(allTags)): + allTags[i] = int(allTags[i][3:-1]) + +allTags.sort() + +allTags.remove(rev) + +for rev in allTags: + print os.popen("git tag -d p4/%s" % rev).read() -- cgit v1.2.3 From fc21f8a1dab090ceb63c479705104cbb95585a2f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 11 Feb 2007 18:04:39 +0100 Subject: Create lightweight git tags (using the "reset" trick) for the incremental import instead of full-blown ones. Also fix parsing the output of git name-rev for figuring out the last imported p4 change number. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 1f19cbc560..989513888a 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -170,9 +170,14 @@ if len(changeRange) == 0: try: sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) output = sout.read() + if output.endswith("\n"): + output = output[:-1] tagIdx = output.index(" tags/p4/") - caretIdx = output.index("^") - rev = int(output[tagIdx + 9 : caretIdx]) + 1 + caretIdx = output.find("^") + endPos = len(output) + if caretIdx != -1: + endPos = caretIdx + rev = int(output[tagIdx + 9 : endPos]) + 1 changeRange = "@%s,#head" % rev initialParent = os.popen("git-rev-parse %s" % branch).read()[:-1] initialTag = "p4/%s" % (int(rev) - 1) @@ -247,10 +252,9 @@ else: print "" -gitStream.write("tag p4/%s\n" % lastChange) -gitStream.write("from %s\n" % branch); -gitStream.write("tagger %s\n" % lastCommitter); -gitStream.write("data 0\n\n") +gitStream.write("reset refs/tags/p4/%s\n" % lastChange) +gitStream.write("from %s\n\n" % branch); + gitStream.close() gitOutput.close() -- cgit v1.2.3 From 12d04ca7da3bcfec123fb56d3d143ccda729a7f4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 11 Feb 2007 21:35:34 +0100 Subject: Cleanups, remove unused variable. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 989513888a..61b9e67e54 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -50,7 +50,6 @@ revision = "" users = {} initialParent = "" lastChange = "" -lastCommitter = "" initialTag = "" if prefix.find("@") != -1: @@ -98,7 +97,6 @@ def commit(details): global initialParent global users global lastChange - global lastCommitter epoch = details["time"] author = details["user"] @@ -153,7 +151,6 @@ def commit(details): gitStream.write("\n") lastChange = details["change"] - lastCommitter = committer def getUserMap(): users = {} -- cgit v1.2.3 From 44b3add6519d6a85fb75b90173ecd6e7ec1ba650 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Feb 2007 20:28:58 +0100 Subject: Code cleanups. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 61b9e67e54..07d6e53852 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -93,7 +93,20 @@ def p4Cmd(cmd): result.update(entry) return result; -def commit(details): +def extractFilesFromCommit(commit): + files = [] + fnum = 0 + while commit.has_key("depotFile%s" % fnum): + file = {} + file["path"] = commit["depotFile%s" % fnum] + file["rev"] = commit["rev%s" % fnum] + file["action"] = commit["action%s" % fnum] + file["type"] = commit["type%s" % fnum] + files.append(file) + fnum = fnum + 1 + return files + +def commit(details, files, branch): global initialParent global users global lastChange @@ -119,24 +132,22 @@ def commit(details): gitStream.write("from %s\n" % initialParent) initialParent = "" - fnum = 0 - while details.has_key("depotFile%s" % fnum): - path = details["depotFile%s" % fnum] + for file in files: + path = file["path"] if not path.startswith(prefix): print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change) - fnum = fnum + 1 continue - rev = details["rev%s" % fnum] + rev = file["rev"] depotPath = path + "#" + rev relPath = path[len(prefix):] - action = details["action%s" % fnum] + action = file["action"] if action == "delete": gitStream.write("D %s\n" % relPath) else: mode = 644 - if details["type%s" % fnum].startswith("x"): + if file["type"].startswith("x"): mode = 755 data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -146,8 +157,6 @@ def commit(details): gitStream.write(data) gitStream.write("\n") - fnum = fnum + 1 - gitStream.write("\n") lastChange = details["change"] @@ -215,7 +224,7 @@ if len(revision) > 0: details["change"] = newestRevision try: - commit(details) + commit(details, extractFilesFromCommit(details), branch) except: print gitError.read() @@ -242,7 +251,7 @@ else: cnt = cnt + 1 try: - commit(description) + commit(description, extractFilesFromCommit(description), branch) except: print gitError.read() sys.exit(1) -- cgit v1.2.3 From 766887e110898d4ec6f08d19061db323a1ac2b27 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Feb 2007 21:04:59 +0100 Subject: Started work on p4 branch detection (experimental!). Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 59 +++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 07d6e53852..01bf5baf9a 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -14,11 +14,12 @@ import marshal, popen2, getopt branch = "refs/heads/master" prefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() +detectBranches = False if len(prefix) != 0: prefix = prefix[:-1] try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=" ]) + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -26,6 +27,8 @@ except getopt.GetoptError: for o, a in opts: if o == "--branch": branch = "refs/heads/" + a + elif o == "--detect-branches": + detectBranches = True if len(args) == 0 and len(prefix) != 0: print "[using previously specified depot path %s]" % prefix @@ -97,8 +100,13 @@ def extractFilesFromCommit(commit): files = [] fnum = 0 while commit.has_key("depotFile%s" % fnum): + path = commit["depotFile%s" % fnum] + if not path.startswith(prefix): + print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change) + continue + file = {} - file["path"] = commit["depotFile%s" % fnum] + file["path"] = path file["rev"] = commit["rev%s" % fnum] file["action"] = commit["action%s" % fnum] file["type"] = commit["type%s" % fnum] @@ -106,7 +114,37 @@ def extractFilesFromCommit(commit): fnum = fnum + 1 return files -def commit(details, files, branch): +def branchesForCommit(files): + branches = set() + + for file in files: + relativePath = file["path"][len(prefix):] + # strip off the filename + relativePath = relativePath[0:relativePath.rfind("/")] + + if len(branches) == 0: + branches.add(relativePath) + continue + + ###### this needs more testing :) + knownBranch = False + for branch in branches: + if relativePath == branch: + knownBranch = True + break + if relativePath.startswith(branch): + knownBranch = True + break + if branch.startswith(relativePath): + branches.remove(branch) + break + + if not knownBranch: + branches.add(relativePath) + + return branches + +def commit(details, files, branch, prefix): global initialParent global users global lastChange @@ -134,10 +172,6 @@ def commit(details, files, branch): for file in files: path = file["path"] - if not path.startswith(prefix): - print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change) - continue - rev = file["rev"] depotPath = path + "#" + rev relPath = path[len(prefix):] @@ -224,7 +258,7 @@ if len(revision) > 0: details["change"] = newestRevision try: - commit(details, extractFilesFromCommit(details), branch) + commit(details, extractFilesFromCommit(details), branch, prefix) except: print gitError.read() @@ -251,7 +285,14 @@ else: cnt = cnt + 1 try: - commit(description, extractFilesFromCommit(description), branch) + files = extractFilesFromCommit(description) + if detectBranches: + for branch in branchesForCommit(files): + branchPrefix = prefix + branch + "/" + branch = "refs/heads/" + branch + commit(description, files, branch, branchPrefix) + else: + commit(description, files, branch, prefix) except: print gitError.read() sys.exit(1) -- cgit v1.2.3 From dcacf8b447be74efaa5335f42c502ee1c397c436 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Feb 2007 21:41:45 +0100 Subject: More fixes in heuristic p4 branch detection based on common path components. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 108 +++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 34 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 01bf5baf9a..f9653f1344 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -12,11 +12,12 @@ import os, string, sys, time import marshal, popen2, getopt +knownBranches = set() branch = "refs/heads/master" -prefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() +globalPrefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() detectBranches = False -if len(prefix) != 0: - prefix = prefix[:-1] +if len(globalPrefix) != 0: + globalPrefix = globalPrefix[:-1] try: opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches" ]) @@ -30,8 +31,8 @@ for o, a in opts: elif o == "--detect-branches": detectBranches = True -if len(args) == 0 and len(prefix) != 0: - print "[using previously specified depot path %s]" % prefix +if len(args) == 0 and len(globalPrefix) != 0: + print "[using previously specified depot path %s]" % globalPrefix elif len(args) != 1: print "usage: %s //depot/path[@revRange]" % sys.argv[0] print "\n example:" @@ -43,10 +44,10 @@ elif len(args) != 1: print "" sys.exit(1) else: - if len(prefix) != 0 and prefix != args[0]: - print "previous import used depot path %s and now %s was specified. this doesn't work!" % (prefix, args[0]) + if len(globalPrefix) != 0 and globalPrefix != args[0]: + print "previous import used depot path %s and now %s was specified. this doesn't work!" % (globalPrefix, args[0]) sys.exit(1) - prefix = args[0] + globalPrefix = args[0] changeRange = "" revision = "" @@ -55,27 +56,27 @@ initialParent = "" lastChange = "" initialTag = "" -if prefix.find("@") != -1: - atIdx = prefix.index("@") - changeRange = prefix[atIdx:] +if globalPrefix.find("@") != -1: + atIdx = globalPrefix.index("@") + changeRange = globalPrefix[atIdx:] if changeRange == "@all": changeRange = "" elif changeRange.find(",") == -1: revision = changeRange changeRange = "" - prefix = prefix[0:atIdx] -elif prefix.find("#") != -1: - hashIdx = prefix.index("#") - revision = prefix[hashIdx:] - prefix = prefix[0:hashIdx] + globalPrefix = globalPrefix[0:atIdx] +elif globalPrefix.find("#") != -1: + hashIdx = globalPrefix.index("#") + revision = globalPrefix[hashIdx:] + globalPrefix = globalPrefix[0:hashIdx] elif len(previousDepotPath) == 0: revision = "#head" -if prefix.endswith("..."): - prefix = prefix[:-3] +if globalPrefix.endswith("..."): + globalPrefix = globalPrefix[:-3] -if not prefix.endswith("/"): - prefix += "/" +if not globalPrefix.endswith("/"): + globalPrefix += "/" def p4CmdList(cmd): pipe = os.popen("p4 -G %s" % cmd, "rb") @@ -101,8 +102,8 @@ def extractFilesFromCommit(commit): fnum = 0 while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] - if not path.startswith(prefix): - print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, prefix, change) + if not path.startswith(globalPrefix): + print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) continue file = {} @@ -118,7 +119,7 @@ def branchesForCommit(files): branches = set() for file in files: - relativePath = file["path"][len(prefix):] + relativePath = file["path"][len(globalPrefix):] # strip off the filename relativePath = relativePath[0:relativePath.rfind("/")] @@ -144,7 +145,7 @@ def branchesForCommit(files): return branches -def commit(details, files, branch, prefix): +def commit(details, files, branch, branchPrefix): global initialParent global users global lastChange @@ -163,7 +164,7 @@ def commit(details, files, branch, prefix): gitStream.write("data < 0: @@ -172,9 +173,47 @@ def commit(details, files, branch, prefix): for file in files: path = file["path"] + if not path.startswith(branchPrefix): + continue + action = file["action"] + if action != "integrate" and action != "branch": + continue + rev = file["rev"] + depotPath = path + "#" + rev + + log = p4CmdList("filelog \"%s\"" % depotPath) + if len(log) != 1: + print "eek! I got confused by the filelog of %s" % depotPath + sys.exit(1); + + log = log[0] + if log["action0"] != action: + print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) + sys.exit(1); + + if not log["how0,0"].endswith(" from"): + print "eek! file %s was not branched but instead: %s" % (depotPath, log["how0,0"]) + sys.exit(1); + + source = log["file0,0"] + if source.startswith(branchPrefix): + continue + + relPath = source[len(globalPrefix):] + + for branch in knownBranches: + if relPath.startswith(branch): + gitStream.write("merge refs/heads/%s\n" % branch) + break + + for file in files: + path = file["path"] + if not path.startswith(branchPrefix): + print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, change) + continue rev = file["rev"] depotPath = path + "#" + rev - relPath = path[len(prefix):] + relPath = path[len(branchPrefix):] action = file["action"] if action == "delete": @@ -234,15 +273,15 @@ if tzsign != '+' and tzsign != '-': gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") if len(revision) > 0: - print "Doing initial import of %s from revision %s" % (prefix, revision) + print "Doing initial import of %s from revision %s" % (globalPrefix, revision) details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = "Initial import of %s from the state at revision %s" % (prefix, revision) + details["desc"] = "Initial import of %s from the state at revision %s" % (globalPrefix, revision) details["change"] = revision newestRevision = 0 fileCnt = 0 - for info in p4CmdList("files %s...%s" % (prefix, revision)): + for info in p4CmdList("files %s...%s" % (globalPrefix, revision)): change = int(info["change"]) if change > newestRevision: newestRevision = change @@ -258,12 +297,12 @@ if len(revision) > 0: details["change"] = newestRevision try: - commit(details, extractFilesFromCommit(details), branch, prefix) + commit(details, extractFilesFromCommit(details), branch, globalPrefix) except: print gitError.read() else: - output = os.popen("p4 changes %s...%s" % (prefix, changeRange)).readlines() + output = os.popen("p4 changes %s...%s" % (globalPrefix, changeRange)).readlines() changes = [] for line in output: @@ -288,11 +327,12 @@ else: files = extractFilesFromCommit(description) if detectBranches: for branch in branchesForCommit(files): - branchPrefix = prefix + branch + "/" + knownBranches.add(branch) + branchPrefix = globalPrefix + branch + "/" branch = "refs/heads/" + branch commit(description, files, branch, branchPrefix) else: - commit(description, files, branch, prefix) + commit(description, files, branch, globalPrefix) except: print gitError.read() sys.exit(1) @@ -307,7 +347,7 @@ gitStream.close() gitOutput.close() gitError.close() -os.popen("git-repo-config p4.depotpath %s" % prefix).read() +os.popen("git-repo-config p4.depotpath %s" % globalPrefix).read() if len(initialTag) > 0: os.popen("git tag -d %s" % initialTag).read() -- cgit v1.2.3 From 53b03239aa899677bcadf69f757354b72797e457 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Feb 2007 21:44:02 +0100 Subject: After marking a p4 branch as merged don't ever merge it in git again. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index f9653f1344..5838ca3c68 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -171,6 +171,8 @@ def commit(details, files, branch, branchPrefix): gitStream.write("from %s\n" % initialParent) initialParent = "" + mergedBranches = set() + for file in files: path = file["path"] if not path.startswith(branchPrefix): @@ -202,8 +204,9 @@ def commit(details, files, branch, branchPrefix): relPath = source[len(globalPrefix):] for branch in knownBranches: - if relPath.startswith(branch): + if relPath.startswith(branch) and branch not in mergedBranches: gitStream.write("merge refs/heads/%s\n" % branch) + mergedBranches.add(branch) break for file in files: -- cgit v1.2.3 From 77083daac72612ab8ded6cba82a72924f3a867b4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Feb 2007 21:56:46 +0100 Subject: Set git fast-import marks for every imported change for future use. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 5838ca3c68..488533b07e 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -154,6 +154,7 @@ def commit(details, files, branch, branchPrefix): author = details["user"] gitStream.write("commit %s\n" % branch) + gitStream.write("mark :%s\n" % details["change"]) committer = "" if author in users: committer = "%s %s %s" % (users[author], epoch, tz) -- cgit v1.2.3 From 930d3cc94e98ccecfa8762ef79f7c29af4a72769 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Feb 2007 22:05:21 +0100 Subject: When trying to map p4 integrations to git merges just record it as a single merge with the newest p4 change as secondary parent. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 488533b07e..0a3e5c9934 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -172,7 +172,8 @@ def commit(details, files, branch, branchPrefix): gitStream.write("from %s\n" % initialParent) initialParent = "" - mergedBranches = set() + #mergedBranches = set() + merge = 0 for file in files: path = file["path"] @@ -202,13 +203,28 @@ def commit(details, files, branch, branchPrefix): if source.startswith(branchPrefix): continue - relPath = source[len(globalPrefix):] + lastSourceRev = log["erev0,0"] - for branch in knownBranches: - if relPath.startswith(branch) and branch not in mergedBranches: - gitStream.write("merge refs/heads/%s\n" % branch) - mergedBranches.add(branch) - break + sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) + if len(sourceLog) != 1: + print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) + sys.exit(1); + sourceLog = sourceLog[0] + + change = int(sourceLog["change0"]) + if change > merge: + merge = change + +# relPath = source[len(globalPrefix):] +# +# for branch in knownBranches: +# if relPath.startswith(branch) and branch not in mergedBranches: +# gitStream.write("merge refs/heads/%s\n" % branch) +# mergedBranches.add(branch) +# break + + if merge != 0: + gitStream.write("merge :%s\n" % merge) for file in files: path = file["path"] -- cgit v1.2.3 From 0563a4538a33c7ae9da41628d6f8849c38a2f935 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 14 Feb 2007 17:13:17 +0100 Subject: Make it possible to specify the p4 changes to import through a text file (for debugging) and made various improvements to the branch/merge heuristic detection. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 128 ++++++++++++++++++++++++---------- 1 file changed, 90 insertions(+), 38 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 0a3e5c9934..b5dc6f6767 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -13,14 +13,16 @@ import os, string, sys, time import marshal, popen2, getopt knownBranches = set() +committedChanges = set() branch = "refs/heads/master" globalPrefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() detectBranches = False +changesFile = "" if len(globalPrefix) != 0: globalPrefix = globalPrefix[:-1] try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches" ]) + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -30,6 +32,8 @@ for o, a in opts: branch = "refs/heads/" + a elif o == "--detect-branches": detectBranches = True + elif o == "--changesfile": + changesFile = a if len(args) == 0 and len(globalPrefix) != 0: print "[using previously specified depot path %s]" % globalPrefix @@ -53,7 +57,7 @@ changeRange = "" revision = "" users = {} initialParent = "" -lastChange = "" +lastChange = 0 initialTag = "" if globalPrefix.find("@") != -1: @@ -104,6 +108,7 @@ def extractFilesFromCommit(commit): path = commit["depotFile%s" % fnum] if not path.startswith(globalPrefix): print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) + fnum = fnum + 1 continue file = {} @@ -115,7 +120,15 @@ def extractFilesFromCommit(commit): fnum = fnum + 1 return files +def isSubPathOf(first, second): + if not first.startswith(second): + return False + if first == second: + return True + return first[len(second)] == "/" + def branchesForCommit(files): + global knownBranches branches = set() for file in files: @@ -123,9 +136,10 @@ def branchesForCommit(files): # strip off the filename relativePath = relativePath[0:relativePath.rfind("/")] - if len(branches) == 0: - branches.add(relativePath) - continue +# if len(branches) == 0: +# branches.add(relativePath) +# knownBranches.add(relativePath) +# continue ###### this needs more testing :) knownBranch = False @@ -133,15 +147,32 @@ def branchesForCommit(files): if relativePath == branch: knownBranch = True break - if relativePath.startswith(branch): +# if relativePath.startswith(branch): + if isSubPathOf(relativePath, branch): knownBranch = True break - if branch.startswith(relativePath): +# if branch.startswith(relativePath): + if isSubPathOf(branch, relativePath): branches.remove(branch) break - if not knownBranch: - branches.add(relativePath) + if knownBranch: + continue + + for branch in knownBranches: + #if relativePath.startswith(branch): + if isSubPathOf(relativePath, branch): + if len(branches) == 0: + relativePath = branch + else: + knownBranch = True + break + + if knownBranch: + continue + + branches.add(relativePath) + knownBranches.add(relativePath) return branches @@ -149,12 +180,14 @@ def commit(details, files, branch, branchPrefix): global initialParent global users global lastChange + global committedChanges epoch = details["time"] author = details["user"] gitStream.write("commit %s\n" % branch) gitStream.write("mark :%s\n" % details["change"]) + committedChanges.add(int(details["change"])) committer = "" if author in users: committer = "%s %s %s" % (users[author], epoch, tz) @@ -173,9 +206,11 @@ def commit(details, files, branch, branchPrefix): initialParent = "" #mergedBranches = set() - merge = 0 + merges = set() for file in files: + if lastChange == 0: + continue path = file["path"] if not path.startswith(branchPrefix): continue @@ -195,9 +230,14 @@ def commit(details, files, branch, branchPrefix): print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) sys.exit(1); - if not log["how0,0"].endswith(" from"): - print "eek! file %s was not branched but instead: %s" % (depotPath, log["how0,0"]) - sys.exit(1); + branchAction = log["how0,0"] +# if branchAction == "branch into" or branchAction == "ignored": +# continue # ignore for branching + + if not branchAction.endswith(" from"): + continue # ignore for branching +# print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) +# sys.exit(1); source = log["file0,0"] if source.startswith(branchPrefix): @@ -212,8 +252,7 @@ def commit(details, files, branch, branchPrefix): sourceLog = sourceLog[0] change = int(sourceLog["change0"]) - if change > merge: - merge = change + merges.add(change) # relPath = source[len(globalPrefix):] # @@ -223,13 +262,14 @@ def commit(details, files, branch, branchPrefix): # mergedBranches.add(branch) # break - if merge != 0: - gitStream.write("merge :%s\n" % merge) + for merge in merges: + if merge in committedChanges: + gitStream.write("merge :%s\n" % merge) for file in files: path = file["path"] if not path.startswith(branchPrefix): - print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, change) + print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) continue rev = file["rev"] depotPath = path + "#" + rev @@ -252,7 +292,7 @@ def commit(details, files, branch, branchPrefix): gitStream.write("\n") - lastChange = details["change"] + lastChange = int(details["change"]) def getUserMap(): users = {} @@ -322,14 +362,26 @@ if len(revision) > 0: print gitError.read() else: - output = os.popen("p4 changes %s...%s" % (globalPrefix, changeRange)).readlines() - changes = [] - for line in output: - changeNum = line.split(" ")[1] - changes.append(changeNum) - changes.reverse() + if len(changesFile) > 0: + output = open(changesFile).readlines() + changeSet = set() + for line in output: + changeSet.add(int(line)) + + for change in changeSet: + changes.append(change) + + changes.sort() + else: + output = os.popen("p4 changes %s...%s" % (globalPrefix, changeRange)).readlines() + + for line in output: + changeNum = line.split(" ")[1] + changes.append(changeNum) + + changes.reverse() if len(changes) == 0: print "no changes to import!" @@ -343,19 +395,19 @@ else: sys.stdout.flush() cnt = cnt + 1 - try: - files = extractFilesFromCommit(description) - if detectBranches: - for branch in branchesForCommit(files): - knownBranches.add(branch) - branchPrefix = globalPrefix + branch + "/" - branch = "refs/heads/" + branch - commit(description, files, branch, branchPrefix) - else: - commit(description, files, branch, globalPrefix) - except: - print gitError.read() - sys.exit(1) +# try: + files = extractFilesFromCommit(description) + if detectBranches: + for branch in branchesForCommit(files): + knownBranches.add(branch) + branchPrefix = globalPrefix + branch + "/" + branch = "refs/heads/" + branch + commit(description, files, branch, branchPrefix) + else: + commit(description, files, branch, globalPrefix) +# except: +# print gitError.read() +# sys.exit(1) print "" -- cgit v1.2.3 From f1e9b5345efade26961289860ced9e5c428360a4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Feb 2007 02:16:14 +1000 Subject: Use sets.Set() instead of set() to run also with older versions of Python. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index b5dc6f6767..76c4b9d323 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -11,9 +11,10 @@ # import os, string, sys, time import marshal, popen2, getopt +from sets import Set; -knownBranches = set() -committedChanges = set() +knownBranches = Set() +committedChanges = Set() branch = "refs/heads/master" globalPrefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() detectBranches = False @@ -129,7 +130,7 @@ def isSubPathOf(first, second): def branchesForCommit(files): global knownBranches - branches = set() + branches = Set() for file in files: relativePath = file["path"][len(globalPrefix):] @@ -205,8 +206,8 @@ def commit(details, files, branch, branchPrefix): gitStream.write("from %s\n" % initialParent) initialParent = "" - #mergedBranches = set() - merges = set() + #mergedBranches = Set() + merges = Set() for file in files: if lastChange == 0: @@ -366,7 +367,7 @@ else: if len(changesFile) > 0: output = open(changesFile).readlines() - changeSet = set() + changeSet = Set() for line in output: changeSet.add(int(line)) -- cgit v1.2.3 From 90dc3dfdc8ab903f5eb12f7081dd43b67c8c112a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 18 Feb 2007 01:18:22 +1000 Subject: Fix single-branch imports by skipping the branch/merge detection correctly. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 76c4b9d323..f37fdcf96e 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -210,7 +210,7 @@ def commit(details, files, branch, branchPrefix): merges = Set() for file in files: - if lastChange == 0: + if lastChange == 0 or not detectBranches: continue path = file["path"] if not path.startswith(branchPrefix): -- cgit v1.2.3 From 9c9fec3d28a77240fc7dbf44ba2f0805fed5f012 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Feb 2007 17:51:07 +0100 Subject: Added p4 delete behavioural emulation as todo item. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index f37fdcf96e..6fd86cf735 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -8,6 +8,8 @@ # TODO: # - support integrations (at least p4i) # - support p4 submit (hah!) +# - emulate p4's delete behavior: if a directory becomes empty delete it. continue +# with parent dir until non-empty dir is found. # import os, string, sys, time import marshal, popen2, getopt -- cgit v1.2.3 From 161958cc2f71eefbf514b2265741f7d2eb1a5f0b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Feb 2007 09:03:39 +0100 Subject: Added support for --silent so that p4-fast-export can be called from cronjobs. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 6fd86cf735..b39548917a 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -15,6 +15,7 @@ import os, string, sys, time import marshal, popen2, getopt from sets import Set; +silent = False knownBranches = Set() committedChanges = Set() branch = "refs/heads/master" @@ -25,7 +26,7 @@ if len(globalPrefix) != 0: globalPrefix = globalPrefix[:-1] try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=" ]) + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -37,6 +38,8 @@ for o, a in opts: detectBranches = True elif o == "--changesfile": changesFile = a + elif o == "--silent": + silent= True if len(args) == 0 and len(globalPrefix) != 0: print "[using previously specified depot path %s]" % globalPrefix @@ -110,7 +113,8 @@ def extractFilesFromCommit(commit): while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] if not path.startswith(globalPrefix): - print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) + if not silent: + print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) fnum = fnum + 1 continue @@ -272,7 +276,8 @@ def commit(details, files, branch, branchPrefix): for file in files: path = file["path"] if not path.startswith(branchPrefix): - print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) + if not silent: + print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) continue rev = file["rev"] depotPath = path + "#" + rev @@ -387,15 +392,17 @@ else: changes.reverse() if len(changes) == 0: - print "no changes to import!" + if not silent: + print "no changes to import!" sys.exit(1) cnt = 1 for change in changes: description = p4Cmd("describe %s" % change) - sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) - sys.stdout.flush() + if not silent: + sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() cnt = cnt + 1 # try: @@ -412,7 +419,8 @@ else: # print gitError.read() # sys.exit(1) -print "" +if not silent: + print "" gitStream.write("reset refs/tags/p4/%s\n" % lastChange) gitStream.write("from %s\n\n" % branch); -- cgit v1.2.3 From 47e33ec082a4ee5d2925760cb1150b25f4ef392e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Feb 2007 09:22:36 +0100 Subject: More work in --silent support. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index b39548917a..0541f84188 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -42,7 +42,8 @@ for o, a in opts: silent= True if len(args) == 0 and len(globalPrefix) != 0: - print "[using previously specified depot path %s]" % globalPrefix + if not silent: + print "[using previously specified depot path %s]" % globalPrefix elif len(args) != 1: print "usage: %s //depot/path[@revRange]" % sys.argv[0] print "\n example:" -- cgit v1.2.3 From 5ea919de226177e52f62d1c92208ad9687fd76d7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Feb 2007 10:20:53 +0100 Subject: Don't print a plain newline at the end of the execution (avoids bogus cron error mails). Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 0541f84188..8f5b8fe314 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -332,8 +332,6 @@ if len(changeRange) == 0: except: pass -sys.stderr.write("\n") - tz = - time.timezone / 36 tzsign = ("%s" % tz)[0] if tzsign != '+' and tzsign != '-': -- cgit v1.2.3 From 6392a40e5ec1cc1190f5870f6d7c9cc3710dfd46 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 7 Mar 2007 19:58:54 +0100 Subject: Adjust the output parsing of git name-rev to handle the output of the latest git version. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-clean-tags.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-clean-tags.py b/contrib/fast-import/p4-clean-tags.py index 0be51f6405..924ff89cc6 100755 --- a/contrib/fast-import/p4-clean-tags.py +++ b/contrib/fast-import/p4-clean-tags.py @@ -25,7 +25,10 @@ for o, a in opts: sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) output = sout.read() tagIdx = output.index(" tags/p4/") -caretIdx = output.index("^") +try: + caretIdx = output.index("^") +except: + caretIdx = len(output) - 1 rev = int(output[tagIdx + 9 : caretIdx]) allTags = os.popen("git tag -l p4/").readlines() -- cgit v1.2.3 From 3ef674bd4b2f651d769cd5b464ba26313eab290a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 8 Mar 2007 21:27:59 +0100 Subject: Work in progress on detecting branches. Added a disk-cache p4 output so debugging imports is faster. Added --known-branches commandline option for pre-defining branches. Various other fixes... Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 160 ++++++++++++++++++++++------------ 1 file changed, 103 insertions(+), 57 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 8f5b8fe314..a2cca31173 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -11,12 +11,15 @@ # - emulate p4's delete behavior: if a directory becomes empty delete it. continue # with parent dir until non-empty dir is found. # -import os, string, sys, time -import marshal, popen2, getopt +import os, string, sys, time, os.path +import marshal, popen2, getopt, sha from sets import Set; +cacheDebug = False + silent = False knownBranches = Set() +createdBranches = Set() committedChanges = Set() branch = "refs/heads/master" globalPrefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() @@ -26,7 +29,7 @@ if len(globalPrefix) != 0: globalPrefix = globalPrefix[:-1] try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent" ]) + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent", "known-branches=" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -40,6 +43,9 @@ for o, a in opts: changesFile = a elif o == "--silent": silent= True + elif o == "--known-branches": + for branch in o.split(","): + knownBranches.add(branch) if len(args) == 0 and len(globalPrefix) != 0: if not silent: @@ -89,8 +95,37 @@ if globalPrefix.endswith("..."): if not globalPrefix.endswith("/"): globalPrefix += "/" +def p4File(depotPath): + cacheKey = "/tmp/p4cache/data-" + sha.new(depotPath).hexdigest() + + data = 0 + try: + if not cacheDebug: + raise + data = open(cacheKey, "rb").read() + except: + data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + if cacheDebug: + open(cacheKey, "wb").write(data) + + return data + def p4CmdList(cmd): - pipe = os.popen("p4 -G %s" % cmd, "rb") + fullCmd = "p4 -G %s" % cmd; + + cacheKey = sha.new(fullCmd).hexdigest() + cacheKey = "/tmp/p4cache/cmd-" + cacheKey + + cached = True + pipe = 0 + try: + if not cacheDebug: + raise + pipe = open(cacheKey, "rb") + except: + cached = False + pipe = os.popen(fullCmd, "rb") + result = [] try: while True: @@ -99,6 +134,13 @@ def p4CmdList(cmd): except EOFError: pass pipe.close() + + if not cached and cacheDebug: + pipe = open(cacheKey, "wb") + for r in result: + marshal.dump(r, pipe) + pipe.close() + return result def p4Cmd(cmd): @@ -114,8 +156,8 @@ def extractFilesFromCommit(commit): while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] if not path.startswith(globalPrefix): - if not silent: - print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) +# if not silent: +# print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) fnum = fnum + 1 continue @@ -184,41 +226,8 @@ def branchesForCommit(files): return branches -def commit(details, files, branch, branchPrefix): - global initialParent - global users - global lastChange - global committedChanges - - epoch = details["time"] - author = details["user"] - - gitStream.write("commit %s\n" % branch) - gitStream.write("mark :%s\n" % details["change"]) - committedChanges.add(int(details["change"])) - committer = "" - if author in users: - committer = "%s %s %s" % (users[author], epoch, tz) - else: - committer = "%s %s %s" % (author, epoch, tz) - - gitStream.write("committer %s\n" % committer) - - gitStream.write("data < 0: - gitStream.write("from %s\n" % initialParent) - initialParent = "" - - #mergedBranches = Set() - merges = Set() - +def findBranchParent(branchPrefix, files): for file in files: - if lastChange == 0 or not detectBranches: - continue path = file["path"] if not path.startswith(branchPrefix): continue @@ -259,26 +268,51 @@ def commit(details, files, branch, branchPrefix): sys.exit(1); sourceLog = sourceLog[0] - change = int(sourceLog["change0"]) - merges.add(change) + relPath = source[len(globalPrefix):] + # strip off the filename + relPath = relPath[0:relPath.rfind("/")] -# relPath = source[len(globalPrefix):] -# -# for branch in knownBranches: -# if relPath.startswith(branch) and branch not in mergedBranches: -# gitStream.write("merge refs/heads/%s\n" % branch) -# mergedBranches.add(branch) -# break + for branch in knownBranches: + if isSubPathOf(relPath, branch): +# print "determined parent branch branch %s due to change in file %s" % (branch, source) + return "refs/heads/%s" % branch +# else: +# print "%s is not a subpath of branch %s" % (relPath, branch) + + return "" + +def commit(details, files, branch, branchPrefix, parent): + global users + global lastChange + global committedChanges + + epoch = details["time"] + author = details["user"] + + gitStream.write("commit %s\n" % branch) + gitStream.write("mark :%s\n" % details["change"]) + committedChanges.add(int(details["change"])) + committer = "" + if author in users: + committer = "%s %s %s" % (users[author], epoch, tz) + else: + committer = "%s %s %s" % (author, epoch, tz) - for merge in merges: - if merge in committedChanges: - gitStream.write("merge :%s\n" % merge) + gitStream.write("committer %s\n" % committer) + + gitStream.write("data < 0: + gitStream.write("from %s\n" % parent) for file in files: path = file["path"] if not path.startswith(branchPrefix): - if not silent: - print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) +# if not silent: +# print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) continue rev = file["rev"] depotPath = path + "#" + rev @@ -292,7 +326,7 @@ def commit(details, files, branch, branchPrefix): if file["type"].startswith("x"): mode = 755 - data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + data = p4File(depotPath) gitStream.write("M %s inline %s\n" % (mode, relPath)) gitStream.write("data %s\n" % len(data)) @@ -410,10 +444,22 @@ else: for branch in branchesForCommit(files): knownBranches.add(branch) branchPrefix = globalPrefix + branch + "/" + + parent = "" + ########### remove cnt!!! + if branch not in createdBranches and cnt > 2: + createdBranches.add(branch) + parent = findBranchParent(branchPrefix, files) + if parent == branch: + parent = "" +# elif len(parent) > 0: +# print "%s branched off of %s" % (branch, parent) + branch = "refs/heads/" + branch - commit(description, files, branch, branchPrefix) + commit(description, files, branch, branchPrefix, parent) else: - commit(description, files, branch, globalPrefix) + commit(description, files, branch, globalPrefix, initialParent) + initialParent = "" # except: # print gitError.read() # sys.exit(1) -- cgit v1.2.3 From 934371385c6e2e26132190542f4082c1655587e7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 8 Mar 2007 21:34:40 +0100 Subject: Changed --known-branches to take a file as argument instead of a comma separated list. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index a2cca31173..5d4ed5cf9c 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -44,8 +44,8 @@ for o, a in opts: elif o == "--silent": silent= True elif o == "--known-branches": - for branch in o.split(","): - knownBranches.add(branch) + for branch in open(a).readlines(): + knownBranches.add(branch[:-1]) if len(args) == 0 and len(globalPrefix) != 0: if not silent: -- cgit v1.2.3 From a0f22e996c9d2f0e2a4a8feae852478f4aa667b5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 09:49:19 +0100 Subject: Fixed p4-debug file extension. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-debug.p4 | 25 ------------------------- contrib/fast-import/p4-debug.py | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 25 deletions(-) delete mode 100755 contrib/fast-import/p4-debug.p4 create mode 100755 contrib/fast-import/p4-debug.py (limited to 'contrib') diff --git a/contrib/fast-import/p4-debug.p4 b/contrib/fast-import/p4-debug.p4 deleted file mode 100755 index 8fb159fd6a..0000000000 --- a/contrib/fast-import/p4-debug.p4 +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/python -# -# p4-debug.py -# -# Author: Simon Hausmann -# License: MIT -# -# executes a p4 command with -G and prints the resulting python dicts -# -import os, string, sys -import marshal, popen2 - -cmd = "" -for arg in sys.argv[1:]: - cmd += arg + " " - -pipe = os.popen("p4 -G %s" % cmd, "rb") -try: - while True: - entry = marshal.load(pipe) - print entry -except EOFError: - pass -pipe.close() - diff --git a/contrib/fast-import/p4-debug.py b/contrib/fast-import/p4-debug.py new file mode 100755 index 0000000000..8fb159fd6a --- /dev/null +++ b/contrib/fast-import/p4-debug.py @@ -0,0 +1,25 @@ +#!/usr/bin/python +# +# p4-debug.py +# +# Author: Simon Hausmann +# License: MIT +# +# executes a p4 command with -G and prints the resulting python dicts +# +import os, string, sys +import marshal, popen2 + +cmd = "" +for arg in sys.argv[1:]: + cmd += arg + " " + +pipe = os.popen("p4 -G %s" % cmd, "rb") +try: + while True: + entry = marshal.load(pipe) + print entry +except EOFError: + pass +pipe.close() + -- cgit v1.2.3 From 59f1d2b52d71c5082359f4caa9af5752f90d26a9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 10:25:34 +0100 Subject: Make the p4 data/command cache configurable through the --cache-debug commandline option. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 5d4ed5cf9c..3d2b42b636 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -29,7 +29,8 @@ if len(globalPrefix) != 0: globalPrefix = globalPrefix[:-1] try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent", "known-branches=" ]) + opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent", "known-branches=", + "cache-debug" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -46,6 +47,8 @@ for o, a in opts: elif o == "--known-branches": for branch in open(a).readlines(): knownBranches.add(branch[:-1]) + elif o == "--cache-debug": + cacheDebug = True if len(args) == 0 and len(globalPrefix) != 0: if not silent: -- cgit v1.2.3 From 478764bc8261857e7eca2deac409e34c2778347a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 10:53:07 +0100 Subject: Minor code cleanups. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 3d2b42b636..bd2f03064b 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -340,6 +340,16 @@ def commit(details, files, branch, branchPrefix, parent): lastChange = int(details["change"]) +def extractFilesInCommitToBranch(files, branchPrefix): + newFiles = [] + + for file in files: + path = file["path"] + if path.startswith(branchPrefix): + newFiles.append(file) + + return newFiles + def getUserMap(): users = {} @@ -448,6 +458,8 @@ else: knownBranches.add(branch) branchPrefix = globalPrefix + branch + "/" + filesForCommit = extractFilesInCommitToBranch(files, branchPrefix) + parent = "" ########### remove cnt!!! if branch not in createdBranches and cnt > 2: @@ -458,10 +470,11 @@ else: # elif len(parent) > 0: # print "%s branched off of %s" % (branch, parent) + branch = "refs/heads/" + branch commit(description, files, branch, branchPrefix, parent) else: - commit(description, files, branch, globalPrefix, initialParent) + commit(description, filesForCommit, branch, globalPrefix, initialParent) initialParent = "" # except: # print gitError.read() -- cgit v1.2.3 From 85a8f1ac3ba10e20fab0e7f38e0d74213535c7ae Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 11:46:26 +0100 Subject: More code cleanups and preparations for more branch detection heuristics. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 61 ++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index bd2f03064b..65b7fca4b6 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -278,7 +278,7 @@ def findBranchParent(branchPrefix, files): for branch in knownBranches: if isSubPathOf(relPath, branch): # print "determined parent branch branch %s due to change in file %s" % (branch, source) - return "refs/heads/%s" % branch + return branch # else: # print "%s is not a subpath of branch %s" % (relPath, branch) @@ -350,6 +350,57 @@ def extractFilesInCommitToBranch(files, branchPrefix): return newFiles +def findBranchSourceHeuristic(files, branch, branchPrefix): + for file in files: + action = file["action"] + if action != "integrate" and action != "branch": + continue + path = file["path"] + rev = file["rev"] + depotPath = path + "#" + rev + + log = p4CmdList("filelog \"%s\"" % depotPath) + if len(log) != 1: + print "eek! I got confused by the filelog of %s" % depotPath + sys.exit(1); + + log = log[0] + if log["action0"] != action: + print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) + sys.exit(1); + + branchAction = log["how0,0"] + + if not branchAction.endswith(" from"): + continue # ignore for branching +# print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) +# sys.exit(1); + + source = log["file0,0"] + if source.startswith(branchPrefix): + continue + + lastSourceRev = log["erev0,0"] + + sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) + if len(sourceLog) != 1: + print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) + sys.exit(1); + sourceLog = sourceLog[0] + + relPath = source[len(globalPrefix):] + # strip off the filename + relPath = relPath[0:relPath.rfind("/")] + + for candidate in knownBranches: + if isSubPathOf(relPath, candidate) and candidate != branch: + return candidate + + return "" + +def changeIsBranchMerge(sourceBranch, destinationBranch, change): + return False + def getUserMap(): users = {} @@ -470,8 +521,16 @@ else: # elif len(parent) > 0: # print "%s branched off of %s" % (branch, parent) + if len(parent) == 0: + parent = findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) + if len(parent) > 0: + print "change %s could be a merge from %s into %s" % (description["change"], parent, branch) + if not changeIsBranchMerge(parent, branch, description["change"]): + parent = "" branch = "refs/heads/" + branch + if len(parent) > 0: + parent = "refs/heads/" + parent commit(description, files, branch, branchPrefix, parent) else: commit(description, filesForCommit, branch, globalPrefix, initialParent) -- cgit v1.2.3 From 43cc31e8a252d3ed326faa28d9a5d406c96d7b0d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 17:46:49 +0100 Subject: More work on branch detection by implementing changeIsBranchMerge(). Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 135 ++++++++++++++++++++++++---------- 1 file changed, 98 insertions(+), 37 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 65b7fca4b6..d0832e8c3d 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -284,7 +284,7 @@ def findBranchParent(branchPrefix, files): return "" -def commit(details, files, branch, branchPrefix, parent): +def commit(details, files, branch, branchPrefix, parent, merged = ""): global users global lastChange global committedChanges @@ -293,7 +293,7 @@ def commit(details, files, branch, branchPrefix, parent): author = details["user"] gitStream.write("commit %s\n" % branch) - gitStream.write("mark :%s\n" % details["change"]) +# gitStream.write("mark :%s\n" % details["change"]) committedChanges.add(int(details["change"])) committer = "" if author in users: @@ -311,6 +311,9 @@ def commit(details, files, branch, branchPrefix, parent): if len(parent) > 0: gitStream.write("from %s\n" % parent) + if len(merged) > 0: + gitStream.write("merge %s\n" % merged) + for file in files: path = file["path"] if not path.startswith(branchPrefix): @@ -399,7 +402,62 @@ def findBranchSourceHeuristic(files, branch, branchPrefix): return "" def changeIsBranchMerge(sourceBranch, destinationBranch, change): - return False + sourceFiles = {} + for file in p4CmdList("files %s...@%s" % (globalPrefix + sourceBranch + "/", change)): + if file["action"] == "delete": + continue + sourceFiles[file["depotFile"]] = file + + destinationFiles = {} + for file in p4CmdList("files %s...@%s" % (globalPrefix + destinationBranch + "/", change)): + destinationFiles[file["depotFile"]] = file + + for fileName in sourceFiles.keys(): + integrations = [] + deleted = False + for integration in p4CmdList("integrated \"%s\"" % fileName): + toFile = integration["fromFile"] # yes, it's true, it's fromFile + if not toFile in destinationFiles: + continue + destFile = destinationFiles[toFile] + if destFile["action"] == "delete": +# print "file %s has been deleted in %s" % (fileName, toFile) + deleted = True + break + + if int(integration["change"]) == change: + integrations.append(integration) + continue + + destRev = int(destFile["rev"]) + + startRev = integration["startFromRev"][1:] + if startRev == "none": + startRev = 0 + else: + startRev = int(startRev) + + endRev = integration["endFromRev"][1:] + if endRev == "none": + endRev = 0 + else: + endRev = int(endRev) + + initialBranch = (destRev == 1 and integration["how"] != "branch into") + inRange = (destRev >= startRev and destRev <= endRev) + newer = (destRev > startRev and destRev > endRev) + + if initialBranch or inRange or newer: + integrations.append(integration) + + if deleted: + continue + + if len(integrations) == 0: + print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) + return False + + return True def getUserMap(): users = {} @@ -502,42 +560,45 @@ else: sys.stdout.flush() cnt = cnt + 1 -# try: - files = extractFilesFromCommit(description) - if detectBranches: - for branch in branchesForCommit(files): - knownBranches.add(branch) - branchPrefix = globalPrefix + branch + "/" - - filesForCommit = extractFilesInCommitToBranch(files, branchPrefix) - - parent = "" - ########### remove cnt!!! - if branch not in createdBranches and cnt > 2: - createdBranches.add(branch) - parent = findBranchParent(branchPrefix, files) - if parent == branch: - parent = "" -# elif len(parent) > 0: -# print "%s branched off of %s" % (branch, parent) - - if len(parent) == 0: - parent = findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) - if len(parent) > 0: - print "change %s could be a merge from %s into %s" % (description["change"], parent, branch) - if not changeIsBranchMerge(parent, branch, description["change"]): + try: + files = extractFilesFromCommit(description) + if detectBranches: + for branch in branchesForCommit(files): + knownBranches.add(branch) + branchPrefix = globalPrefix + branch + "/" + + filesForCommit = extractFilesInCommitToBranch(files, branchPrefix) + + merged = "" + parent = "" + ########### remove cnt!!! + if branch not in createdBranches and cnt > 2: + createdBranches.add(branch) + parent = findBranchParent(branchPrefix, files) + if parent == branch: parent = "" + # elif len(parent) > 0: + # print "%s branched off of %s" % (branch, parent) - branch = "refs/heads/" + branch - if len(parent) > 0: - parent = "refs/heads/" + parent - commit(description, files, branch, branchPrefix, parent) - else: - commit(description, filesForCommit, branch, globalPrefix, initialParent) - initialParent = "" -# except: -# print gitError.read() -# sys.exit(1) + if len(parent) == 0: + merged = findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) + if len(merged) > 0: + print "change %s could be a merge from %s into %s" % (description["change"], merged, branch) + if not changeIsBranchMerge(merged, branch, int(description["change"])): + merged = "" + + branch = "refs/heads/" + branch + if len(parent) > 0: + parent = "refs/heads/" + parent + if len(merged) > 0: + merged = "refs/heads/" + merged + commit(description, files, branch, branchPrefix, parent, merged) + else: + commit(description, filesForCommit, branch, globalPrefix, initialParent) + initialParent = "" + except IOError: + print gitError.read() + sys.exit(1) if not silent: print "" -- cgit v1.2.3 From dd87020bd3969dbac5ae26c527bcf50fe0ebb845 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 21:23:49 +0100 Subject: Reduce the number of false "merges" by skipping "branch from" entries in the integrated output as well as by ignoring integrations of future (newer) changes. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index d0832e8c3d..a45068d362 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -415,6 +415,7 @@ def changeIsBranchMerge(sourceBranch, destinationBranch, change): for fileName in sourceFiles.keys(): integrations = [] deleted = False + integrationCount = 0 for integration in p4CmdList("integrated \"%s\"" % fileName): toFile = integration["fromFile"] # yes, it's true, it's fromFile if not toFile in destinationFiles: @@ -424,10 +425,15 @@ def changeIsBranchMerge(sourceBranch, destinationBranch, change): # print "file %s has been deleted in %s" % (fileName, toFile) deleted = True break + integrationCount += 1 + if integration["how"] == "branch from": + continue if int(integration["change"]) == change: integrations.append(integration) continue + if int(integration["change"]) > change: + continue destRev = int(destFile["rev"]) @@ -453,7 +459,7 @@ def changeIsBranchMerge(sourceBranch, destinationBranch, change): if deleted: continue - if len(integrations) == 0: + if len(integrations) == 0 and integrationCount > 1: print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) return False -- cgit v1.2.3 From 4fe2ca17f779e14554ebca1208310ae4eb0806b5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 10 Mar 2007 21:30:24 +0100 Subject: Split up the cache commandline options into (command) cache and data cache. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index a45068d362..26cd648ef7 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -15,7 +15,8 @@ import os, string, sys, time, os.path import marshal, popen2, getopt, sha from sets import Set; -cacheDebug = False +dataCache = False +commandCache = False silent = False knownBranches = Set() @@ -30,7 +31,7 @@ if len(globalPrefix) != 0: try: opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent", "known-branches=", - "cache-debug" ]) + "cache", "command-cache" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -47,8 +48,11 @@ for o, a in opts: elif o == "--known-branches": for branch in open(a).readlines(): knownBranches.add(branch[:-1]) - elif o == "--cache-debug": - cacheDebug = True + elif o == "--cache": + dataCache = True + commandCache = True + elif o == "--command-cache": + commandCache = True if len(args) == 0 and len(globalPrefix) != 0: if not silent: @@ -103,12 +107,12 @@ def p4File(depotPath): data = 0 try: - if not cacheDebug: + if not dataCache: raise data = open(cacheKey, "rb").read() except: data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() - if cacheDebug: + if dataCache: open(cacheKey, "wb").write(data) return data @@ -122,7 +126,7 @@ def p4CmdList(cmd): cached = True pipe = 0 try: - if not cacheDebug: + if not commandCache: raise pipe = open(cacheKey, "rb") except: @@ -138,7 +142,7 @@ def p4CmdList(cmd): pass pipe.close() - if not cached and cacheDebug: + if not cached and commandCache: pipe = open(cacheKey, "wb") for r in result: marshal.dump(r, pipe) -- cgit v1.2.3 From 0bcff6121d68d8733e9b572eba357bcd8e91e23e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 12 Mar 2007 23:00:34 +0100 Subject: First version of a new script to submit changes back to perforce from git repositories. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 208 +++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100755 contrib/fast-import/p4-git-sync.py (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py new file mode 100755 index 0000000000..8982e45345 --- /dev/null +++ b/contrib/fast-import/p4-git-sync.py @@ -0,0 +1,208 @@ +#!/usr/bin/python +# +# p4-git-sync.py +# +# Author: Simon Hausmann +# License: MIT +# + +import os, string, shelve, stat +import getopt, sys, marshal + +def p4CmdList(cmd): + cmd = "p4 -G %s" % cmd + pipe = os.popen(cmd, "rb") + + result = [] + try: + while True: + entry = marshal.load(pipe) + result.append(entry) + except EOFError: + pass + pipe.close() + + return result + +def p4Cmd(cmd): + list = p4CmdList(cmd) + result = {} + for entry in list: + result.update(entry) + return result; + +try: + opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "--git-dir=", "origin=", "reset", "master=", + "submit-log-subst=" ]) +except getopt.GetoptError: + print "fixme, syntax error" + sys.exit(1) + +logSubstitutions = {} +logSubstitutions[""] = "%log%" +logSubstitutions["\tDetails:"] = "\tDetails: %log%" +gitdir = os.environ.get("GIT_DIR", "") +origin = "origin" +master = "master" +firstTime = True +reset = False + +for o, a in opts: + if o == "--git-dir": + gitdir = a + elif o == "--origin": + origin = a + elif o == "--master": + master = a + elif o == "--continue": + firstTime = False + elif o == "--reset": + reset = True + firstTime = True + elif o == "--submit-log-subst": + key = a.split("%")[0] + value = a.split("%")[1] + logSubstitutions[key] = value + +if len(gitdir) == 0: + gitdir = ".git" +else: + os.environ["GIT_DIR"] = gitdir + +configFile = gitdir + "/p4-git-sync.cfg" + +origin = "origin" +if len(args) == 1: + origin = args[0] + +def die(msg): + sys.stderr.write(msg + "\n") + sys.exit(1) + +def system(cmd): + if os.system(cmd) != 0: + die("command failed: %s" % cmd) + +def check(): + return + if len(p4CmdList("opened ...")) > 0: + die("You have files opened with perforce! Close them before starting the sync.") + +def start(config): + if len(config) > 0 and not reset: + die("Cannot start sync. Previous sync config found at %s" % configFile) + + #if len(os.popen("git-update-index --refresh").read()) > 0: + # die("Your working tree is not clean. Check with git status!") + + commits = [] + for line in os.popen("git-rev-list --no-merges %s..%s" % (origin, master)).readlines(): + commits.append(line[:-1]) + commits.reverse() + + config["commits"] = commits + +# print "Cleaning index..." +# system("git checkout -f") + +def prepareLogMessage(template, message): + result = "" + + substs = logSubstitutions + for k in substs.keys(): + substs[k] = substs[k].replace("%log%", message) + + for line in template.split("\n"): + if line.startswith("#"): + result += line + "\n" + continue + + substituted = False + for key in substs.keys(): + if line.find(key) != -1: + value = substs[key] + if value != "@remove@": + result += line.replace(key, value) + "\n" + substituted = True + break + + if not substituted: + result += line + "\n" + + return result + +def apply(id): + print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) + diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() + filesToAdd = set() + filesToDelete = set() + for line in diff: + modifier = line[0] + path = line[1:].strip() + if modifier == "M": + system("p4 edit %s" % path) + elif modifier == "A": + filesToAdd.add(path) + if path in filesToDelete: + filesToDelete.remove(path) + elif modifier == "D": + filesToDelete.add(path) + if path in filesToAdd: + filesToAdd.remove(path) + else: + die("unknown modifier %s for %s" % (modifier, path)) + + system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") + system("git cherry-pick --no-commit \"%s\"" % id) + + for f in filesToAdd: + system("p4 add %s" % f) + for f in filesToDelete: + system("p4 revert %s" % f) + system("p4 delete %s" % f) + + logMessage = "" + foundTitle = False + for log in os.popen("git-cat-file commit %s" % id).readlines(): + log = log[:-1] + if not foundTitle: + if len(log) == 0: + foundTitle = 1 + continue + + if len(logMessage) > 0: + logMessage += "\t" + logMessage += log + "\n" + + template = os.popen("p4 change -o").read() + fileName = "submit.txt" + file = open(fileName, "w+") + file.write(prepareLogMessage(template, logMessage)) + file.close() + print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) + +check() + +config = shelve.open(configFile, writeback=True) + +if firstTime: + start(config) + +commits = config.get("commits", []) + +if len(commits) > 0: + firstTime = False + commit = commits[0] + commits = commits[1:] + config["commits"] = commits + apply(commit) + +config.close() + +if len(commits) == 0: + if firstTime: + print "No changes found to apply between %s and current HEAD" % origin + else: + print "All changes applied!" + os.remove(configFile) + -- cgit v1.2.3 From 5aba82fd5056fa57f467f3c5fe81fe71ee0e8b99 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 13 Mar 2007 09:14:45 +0100 Subject: Fix git-dir option and allow reading log substitutions from a file Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index 8982e45345..0c0f629a1d 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -32,8 +32,8 @@ def p4Cmd(cmd): return result; try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "--git-dir=", "origin=", "reset", "master=", - "submit-log-subst=" ]) + opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "git-dir=", "origin=", "reset", "master=", + "submit-log-subst=", "log-substitutions=" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -63,6 +63,10 @@ for o, a in opts: key = a.split("%")[0] value = a.split("%")[1] logSubstitutions[key] = value + elif o == "--log-substitutions": + for line in open(a, "r").readlines(): + tokens = line[:-1].split("=") + logSubstitutions[tokens[0]] = tokens[1] if len(gitdir) == 0: gitdir = ".git" -- cgit v1.2.3 From 09a14fb52410cfda029b51832316616b44348505 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 13 Mar 2007 16:36:10 +0100 Subject: Lots of bugfixes to p4-git-sync. Added interactive and dry-run mode. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 90 +++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index 0c0f629a1d..5e307af8c6 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -3,11 +3,13 @@ # p4-git-sync.py # # Author: Simon Hausmann +# Copyright: 2007 Simon Hausmann +# 2007 Trolltech ASA # License: MIT # import os, string, shelve, stat -import getopt, sys, marshal +import getopt, sys, marshal, tempfile def p4CmdList(cmd): cmd = "p4 -G %s" % cmd @@ -33,7 +35,8 @@ def p4Cmd(cmd): try: opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "git-dir=", "origin=", "reset", "master=", - "submit-log-subst=", "log-substitutions=" ]) + "submit-log-subst=", "log-substitutions=", "interactive", + "dry-run" ]) except getopt.GetoptError: print "fixme, syntax error" sys.exit(1) @@ -46,6 +49,8 @@ origin = "origin" master = "master" firstTime = True reset = False +interactive = False +dryRun = False for o, a in opts: if o == "--git-dir": @@ -67,6 +72,10 @@ for o, a in opts: for line in open(a, "r").readlines(): tokens = line[:-1].split("=") logSubstitutions[tokens[0]] = tokens[1] + elif o == "--interactive": + interactive = True + elif o == "--dry-run": + dryRun = True if len(gitdir) == 0: gitdir = ".git" @@ -112,19 +121,16 @@ def start(config): def prepareLogMessage(template, message): result = "" - substs = logSubstitutions - for k in substs.keys(): - substs[k] = substs[k].replace("%log%", message) - for line in template.split("\n"): if line.startswith("#"): result += line + "\n" continue substituted = False - for key in substs.keys(): + for key in logSubstitutions.keys(): if line.find(key) != -1: - value = substs[key] + value = logSubstitutions[key] + value = value.replace("%log%", message) if value != "@remove@": result += line.replace(key, value) + "\n" substituted = True @@ -136,6 +142,7 @@ def prepareLogMessage(template, message): return result def apply(id): + global interactive print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() filesToAdd = set() @@ -158,6 +165,9 @@ def apply(id): system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") system("git cherry-pick --no-commit \"%s\"" % id) + #system("git format-patch --stdout -k \"%s^\"..\"%s\" | git-am -k" % (id, id)) + #system("git branch -D tmp") + #system("git checkout -f -b tmp \"%s^\"" % id) for f in filesToAdd: system("p4 add %s" % f) @@ -168,22 +178,66 @@ def apply(id): logMessage = "" foundTitle = False for log in os.popen("git-cat-file commit %s" % id).readlines(): - log = log[:-1] if not foundTitle: - if len(log) == 0: + if len(log) == 1: foundTitle = 1 continue if len(logMessage) > 0: logMessage += "\t" - logMessage += log + "\n" + logMessage += log template = os.popen("p4 change -o").read() - fileName = "submit.txt" - file = open(fileName, "w+") - file.write(prepareLogMessage(template, logMessage)) - file.close() - print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) + + if interactive: + submitTemplate = prepareLogMessage(template, logMessage) + diff = os.popen("p4 diff -du ...").read() + + for newFile in filesToAdd: + diff += "==== new file ====\n" + diff += "--- /dev/null\n" + diff += "+++ %s\n" % newFile + f = open(newFile, "r") + for line in f.readlines(): + diff += "+" + line + f.close() + + pipe = os.popen("less", "w") + pipe.write(submitTemplate + diff) + pipe.close() + + response = "e" + while response == "e": + response = raw_input("Do you want to submit this change (y/e/n)? ") + if response == "e": + [handle, fileName] = tempfile.mkstemp() + tmpFile = os.fdopen(handle, "w+") + tmpFile.write(submitTemplate) + tmpFile.close() + editor = os.environ.get("EDITOR", "vi") + system(editor + " " + fileName) + tmpFile = open(fileName, "r") + submitTemplate = tmpFile.read() + tmpFile.close() + os.remove(fileName) + + if response == "y" or response == "yes": + if dryRun: + print submitTemplate + raw_input("Press return to continue...") + else: + pipe = os.popen("p4 submit -i", "w") + pipe.write(submitTemplate) + pipe.close() + else: + print "Not submitting!" + interactive = False + else: + fileName = "submit.txt" + file = open(fileName, "w+") + file.write(prepareLogMessage(template, logMessage)) + file.close() + print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) check() @@ -194,12 +248,14 @@ if firstTime: commits = config.get("commits", []) -if len(commits) > 0: +while len(commits) > 0: firstTime = False commit = commits[0] commits = commits[1:] config["commits"] = commits apply(commit) + if not interactive: + break config.close() -- cgit v1.2.3 From 794a913a0096a383560eb06e0fdf1d8331e12c0a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 14 Mar 2007 17:29:46 +0100 Subject: Automatically operate on a temporary branch, needed for cherry-pick to work when applying changes to files that are deleted in the future. Also do some Perforce cleaning Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index 5e307af8c6..02f4b36af3 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -97,7 +97,6 @@ def system(cmd): die("command failed: %s" % cmd) def check(): - return if len(p4CmdList("opened ...")) > 0: die("You have files opened with perforce! Close them before starting the sync.") @@ -115,6 +114,9 @@ def start(config): config["commits"] = commits + print "Creating temporary p4-sync branch from %s ..." % origin + system("git checkout -f -b p4-sync %s" % origin) + # print "Cleaning index..." # system("git checkout -f") @@ -264,5 +266,11 @@ if len(commits) == 0: print "No changes found to apply between %s and current HEAD" % origin else: print "All changes applied!" + print "Deleting temporary p4-sync branch and going back to %s" % master + system("git checkout %s" % master) + system("git branch -D p4-sync") + print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert -a ..." + system("p4 edit ...") + system("p4 revert -a ...") os.remove(configFile) -- cgit v1.2.3 From d7873afdf426b2f430f5635ff460012eb9c00d05 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 14 Mar 2007 17:33:46 +0100 Subject: Be nice and use /usr/bin/env python for the git-p4 scripts Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 2 +- contrib/fast-import/p4-git-sync.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 26cd648ef7..6f7bbb0f32 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # p4-fast-export.py # diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index 02f4b36af3..a4d126fd46 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # p4-git-sync.py # -- cgit v1.2.3 From 4d9e5fcea65f22c6bd9e1f3e5ae2f79c3af613db Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 14 Mar 2007 19:03:16 +0100 Subject: Ignore Apple resource files when importing from perforce to git. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index 6f7bbb0f32..a6dbd3b98d 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -329,6 +329,10 @@ def commit(details, files, branch, branchPrefix, parent, merged = ""): relPath = path[len(branchPrefix):] action = file["action"] + if file["type"] == "apple": + print "\nfile %s is a strange apple file that forks. Ignoring!" %s path + continue + if action == "delete": gitStream.write("D %s\n" % relPath) else: -- cgit v1.2.3 From d566209e7fecffb61a3209766cc937bf027e2c6c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 14 Mar 2007 23:30:23 +0100 Subject: Auto-detect the current git branch before submitting back to perforce. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index a4d126fd46..4f02079895 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -33,6 +33,10 @@ def p4Cmd(cmd): result.update(entry) return result; +def die(msg): + sys.stderr.write(msg + "\n") + sys.exit(1) + try: opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "git-dir=", "origin=", "reset", "master=", "submit-log-subst=", "log-substitutions=", "interactive", @@ -46,7 +50,7 @@ logSubstitutions[""] = "%log%" logSubstitutions["\tDetails:"] = "\tDetails: %log%" gitdir = os.environ.get("GIT_DIR", "") origin = "origin" -master = "master" +master = "" firstTime = True reset = False interactive = False @@ -88,9 +92,12 @@ origin = "origin" if len(args) == 1: origin = args[0] -def die(msg): - sys.stderr.write(msg + "\n") - sys.exit(1) +if len(master) == 0: + sys.stdout.write("Auto-detecting current branch: ") + master = os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] + if len(master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, master)): + die("\nFailed to detect current branch! Aborting!"); + sys.stdout.write("%s\n" % master) def system(cmd): if os.system(cmd) != 0: -- cgit v1.2.3 From f72537f97e81398b597c05f12c112c52e8201b63 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Mar 2007 19:07:06 +0100 Subject: Use p4 revert ... instead of revert -a ... after submitting, to make sure the p4 checkout is clean. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index 4f02079895..e07fcee42c 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -276,8 +276,8 @@ if len(commits) == 0: print "Deleting temporary p4-sync branch and going back to %s" % master system("git checkout %s" % master) system("git branch -D p4-sync") - print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert -a ..." - system("p4 edit ...") - system("p4 revert -a ...") + print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." + system("p4 edit ... >/dev/null") + system("p4 revert ... >/dev/null") os.remove(configFile) -- cgit v1.2.3 From 228d36c92b022ae7941b542731c89db5d9de3eac Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 16 Mar 2007 13:47:46 +0100 Subject: Default to interactive syncing Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index e07fcee42c..ed88debd45 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -39,7 +39,7 @@ def die(msg): try: opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "git-dir=", "origin=", "reset", "master=", - "submit-log-subst=", "log-substitutions=", "interactive", + "submit-log-subst=", "log-substitutions=", "noninteractive", "dry-run" ]) except getopt.GetoptError: print "fixme, syntax error" @@ -53,7 +53,7 @@ origin = "origin" master = "" firstTime = True reset = False -interactive = False +interactive = True dryRun = False for o, a in opts: @@ -76,8 +76,8 @@ for o, a in opts: for line in open(a, "r").readlines(): tokens = line[:-1].split("=") logSubstitutions[tokens[0]] = tokens[1] - elif o == "--interactive": - interactive = True + elif o == "--noninteractive": + interactive = False elif o == "--dry-run": dryRun = True -- cgit v1.2.3 From 09e16455e028ae264347067a8c86a7b9a1e5889c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2007 11:57:07 +0100 Subject: Improved the git dir detection. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-git-sync.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py index ed88debd45..5a3bf90485 100755 --- a/contrib/fast-import/p4-git-sync.py +++ b/contrib/fast-import/p4-git-sync.py @@ -37,6 +37,11 @@ def die(msg): sys.stderr.write(msg + "\n") sys.exit(1) +def tryGitDir(path): + if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): + return True; + return False + try: opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "git-dir=", "origin=", "reset", "master=", "submit-log-subst=", "log-substitutions=", "noninteractive", @@ -86,6 +91,14 @@ if len(gitdir) == 0: else: os.environ["GIT_DIR"] = gitdir +if not tryGitDir(gitdir): + if tryGitDir(gitdir + "/.git"): + gitdir += "/.git" + os.environ["GIT_DIR"] = gitdir + else: + die("fatal: %s seems not to be a git repository." % gitdir) + + configFile = gitdir + "/p4-git-sync.cfg" origin = "origin" -- cgit v1.2.3 From 95d27cb75d70331cc775f37d1f3396cd01e5e238 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2007 12:04:37 +0100 Subject: Pass the right number of arguments to commit, fixes single-branch imports. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py index a6dbd3b98d..9adb88fade 100755 --- a/contrib/fast-import/p4-fast-export.py +++ b/contrib/fast-import/p4-fast-export.py @@ -330,7 +330,7 @@ def commit(details, files, branch, branchPrefix, parent, merged = ""): action = file["action"] if file["type"] == "apple": - print "\nfile %s is a strange apple file that forks. Ignoring!" %s path + print "\nfile %s is a strange apple file that forks. Ignoring!" % path continue if action == "delete": @@ -608,7 +608,7 @@ else: merged = "refs/heads/" + merged commit(description, files, branch, branchPrefix, parent, merged) else: - commit(description, filesForCommit, branch, globalPrefix, initialParent) + commit(description, files, branch, globalPrefix, initialParent) initialParent = "" except IOError: print gitError.read() -- cgit v1.2.3 From 86949eef4088d7b57fe7433568d573a926816f5c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2007 20:59:12 +0100 Subject: Start moving the git-p4 tools into one single script. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 115 +++++++++++++++++++++++++++++++++++ contrib/fast-import/p4-clean-tags.py | 43 ------------- contrib/fast-import/p4-debug.py | 25 -------- 3 files changed, 115 insertions(+), 68 deletions(-) create mode 100755 contrib/fast-import/git-p4.py delete mode 100755 contrib/fast-import/p4-clean-tags.py delete mode 100755 contrib/fast-import/p4-debug.py (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py new file mode 100755 index 0000000000..8008156043 --- /dev/null +++ b/contrib/fast-import/git-p4.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# +# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git. +# +# Author: Simon Hausmann +# License: MIT +# + +import optparse, sys, os, marshal, popen2 + +def p4CmdList(cmd): + cmd = "p4 -G %s" % cmd + pipe = os.popen(cmd, "rb") + + result = [] + try: + while True: + entry = marshal.load(pipe) + result.append(entry) + except EOFError: + pass + pipe.close() + + return result + +def p4Cmd(cmd): + list = p4CmdList(cmd) + result = {} + for entry in list: + result.update(entry) + return result; + +def die(msg): + sys.stderr.write(msg + "\n") + sys.exit(1) + +def currentGitBranch(): + return os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] + +class P4Debug: + def __init__(self): + self.options = [ + ] + + def run(self, args): + for output in p4CmdList(" ".join(args)): + print output + +class P4CleanTags: + def __init__(self): + self.options = [ +# optparse.make_option("--branch", dest="branch", default="refs/heads/master") + ] + def run(self, args): + branch = currentGitBranch() + print "Cleaning out stale p4 import tags..." + sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) + output = sout.read() + try: + tagIdx = output.index(" tags/p4/") + except: + print "Cannot find any p4/* tag. Nothing to do." + sys.exit(0) + + try: + caretIdx = output.index("^") + except: + caretIdx = len(output) - 1 + rev = int(output[tagIdx + 9 : caretIdx]) + + allTags = os.popen("git tag -l p4/").readlines() + for i in range(len(allTags)): + allTags[i] = int(allTags[i][3:-1]) + + allTags.sort() + + allTags.remove(rev) + + for rev in allTags: + print os.popen("git tag -d p4/%s" % rev).read() + + print "%s tags removed." % len(allTags) + +def printUsage(commands): + print "usage: %s [options]" % sys.argv[0] + print "" + print "valid commands: %s" % ", ".join(commands) + print "" + print "Try %s --help for command specific help." % sys.argv[0] + print "" + +commands = { + "debug" : P4Debug(), + "clean-tags" : P4CleanTags() +} + +if len(sys.argv[1:]) == 0: + printUsage(commands.keys()) + sys.exit(2) + +cmd = "" +cmdName = sys.argv[1] +try: + cmd = commands[cmdName] +except KeyError: + print "unknown command %s" % cmdName + print "" + printUsage(commands.keys()) + sys.exit(2) + +parser = optparse.OptionParser("usage: %prog " + cmdName + " [options]", cmd.options) + +(cmd, args) = parser.parse_args(sys.argv[2:], cmd); + +cmd.run(args) diff --git a/contrib/fast-import/p4-clean-tags.py b/contrib/fast-import/p4-clean-tags.py deleted file mode 100755 index 924ff89cc6..0000000000 --- a/contrib/fast-import/p4-clean-tags.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/python -# -# p4-debug.py -# -# Author: Simon Hausmann -# License: MIT -# -# removes unused p4 import tags -# -import os, string, sys -import popen2, getopt - -branch = "refs/heads/master" - -try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=" ]) -except getopt.GetoptError: - print "fixme, syntax error" - sys.exit(1) - -for o, a in opts: - if o == "--branch": - branch = "refs/heads/" + a - -sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) -output = sout.read() -tagIdx = output.index(" tags/p4/") -try: - caretIdx = output.index("^") -except: - caretIdx = len(output) - 1 -rev = int(output[tagIdx + 9 : caretIdx]) - -allTags = os.popen("git tag -l p4/").readlines() -for i in range(len(allTags)): - allTags[i] = int(allTags[i][3:-1]) - -allTags.sort() - -allTags.remove(rev) - -for rev in allTags: - print os.popen("git tag -d p4/%s" % rev).read() diff --git a/contrib/fast-import/p4-debug.py b/contrib/fast-import/p4-debug.py deleted file mode 100755 index 8fb159fd6a..0000000000 --- a/contrib/fast-import/p4-debug.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/python -# -# p4-debug.py -# -# Author: Simon Hausmann -# License: MIT -# -# executes a p4 command with -G and prints the resulting python dicts -# -import os, string, sys -import marshal, popen2 - -cmd = "" -for arg in sys.argv[1:]: - cmd += arg + " " - -pipe = os.popen("p4 -G %s" % cmd, "rb") -try: - while True: - entry = marshal.load(pipe) - print entry -except EOFError: - pass -pipe.close() - -- cgit v1.2.3 From c8c3911685de1185ca7c0afe62bb3964ebc82d38 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2007 21:02:30 +0100 Subject: Provide a little bit of help description for the git-p4 "tools". Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 8008156043..42efc7d9aa 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -41,6 +41,7 @@ class P4Debug: def __init__(self): self.options = [ ] + self.description = "A tool to debug the output of p4 -G." def run(self, args): for output in p4CmdList(" ".join(args)): @@ -51,6 +52,7 @@ class P4CleanTags: self.options = [ # optparse.make_option("--branch", dest="branch", default="refs/heads/master") ] + self.description = "A tool to remove stale unused tags from incremental perforce imports." def run(self, args): branch = currentGitBranch() print "Cleaning out stale p4 import tags..." @@ -108,7 +110,8 @@ except KeyError: printUsage(commands.keys()) sys.exit(2) -parser = optparse.OptionParser("usage: %prog " + cmdName + " [options]", cmd.options) +parser = optparse.OptionParser("usage: %prog " + cmdName + " [options]", cmd.options, + description = cmd.description) (cmd, args) = parser.parse_args(sys.argv[2:], cmd); -- cgit v1.2.3 From 4f5cf76a55ecb3252b0924d0c4a16f3b037908cd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2007 22:25:17 +0100 Subject: First (untested) attempt at migrating p4-git-sync into the final git-p4 script Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 243 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 240 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 42efc7d9aa..593bda822f 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -6,7 +6,10 @@ # License: MIT # -import optparse, sys, os, marshal, popen2 +import optparse, sys, os, marshal, popen2, shelve +import tempfile + +gitdir = os.environ.get("GIT_DIR", "") def p4CmdList(cmd): cmd = "p4 -G %s" % cmd @@ -37,6 +40,15 @@ def die(msg): def currentGitBranch(): return os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] +def isValidGitDir(path): + if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): + return True; + return False + +def system(cmd): + if os.system(cmd) != 0: + die("command failed: %s" % cmd) + class P4Debug: def __init__(self): self.options = [ @@ -83,6 +95,214 @@ class P4CleanTags: print "%s tags removed." % len(allTags) +class P4Sync: + def __init__(self): + self.options = [ + optparse.make_option("--continue", action="store_false", dest="firstTime"), + optparse.make_option("--origin", dest="origin"), + optparse.make_option("--reset", action="store_true", dest="reset"), + optparse.make_option("--master", dest="master"), + optparse.make_option("--log-substitutions", dest="substFile"), + optparse.make_option("--noninteractive", action="store_false"), + optparse.make_option("--dry-run", action="store_true") + ] + self.description = "Submit changes from git to the perforce depot." + self.firstTime = True + self.reset = False + self.interactive = True + self.dryRun = False + self.substFile = "" + self.firstTime = True + self.origin = "origin" + self.master = "" + + self.logSubstitutions = {} + self.logSubstitutions[""] = "%log%" + self.logSubstitutions["\tDetails:"] = "\tDetails: %log%" + + def check(self): + if len(p4CmdList("opened ...")) > 0: + die("You have files opened with perforce! Close them before starting the sync.") + + def start(self): + if len(self.config) > 0 and not self.reset: + die("Cannot start sync. Previous sync config found at %s" % self.configFile) + + commits = [] + for line in os.popen("git-rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): + commits.append(line[:-1]) + commits.reverse() + + self.config["commits"] = commits + + print "Creating temporary p4-sync branch from %s ..." % self.origin + system("git checkout -f -b p4-sync %s" % self.origin) + + def prepareLogMessage(self, template, message): + result = "" + + for line in template.split("\n"): + if line.startswith("#"): + result += line + "\n" + continue + + substituted = False + for key in self.logSubstitutions.keys(): + if line.find(key) != -1: + value = self.logSubstitutions[key] + value = value.replace("%log%", message) + if value != "@remove@": + result += line.replace(key, value) + "\n" + substituted = True + break + + if not substituted: + result += line + "\n" + + return result + + def apply(self, id): + print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) + diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() + filesToAdd = set() + filesToDelete = set() + for line in diff: + modifier = line[0] + path = line[1:].strip() + if modifier == "M": + system("p4 edit %s" % path) + elif modifier == "A": + filesToAdd.add(path) + if path in filesToDelete: + filesToDelete.remove(path) + elif modifier == "D": + filesToDelete.add(path) + if path in filesToAdd: + filesToAdd.remove(path) + else: + die("unknown modifier %s for %s" % (modifier, path)) + + system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") + system("git cherry-pick --no-commit \"%s\"" % id) + + for f in filesToAdd: + system("p4 add %s" % f) + for f in filesToDelete: + system("p4 revert %s" % f) + system("p4 delete %s" % f) + + logMessage = "" + foundTitle = False + for log in os.popen("git-cat-file commit %s" % id).readlines(): + if not foundTitle: + if len(log) == 1: + foundTitle = 1 + continue + + if len(logMessage) > 0: + logMessage += "\t" + logMessage += log + + template = os.popen("p4 change -o").read() + + if self.interactive: + submitTemplate = self.prepareLogMessage(template, logMessage) + diff = os.popen("p4 diff -du ...").read() + + for newFile in filesToAdd: + diff += "==== new file ====\n" + diff += "--- /dev/null\n" + diff += "+++ %s\n" % newFile + f = open(newFile, "r") + for line in f.readlines(): + diff += "+" + line + f.close() + + pipe = os.popen("less", "w") + pipe.write(submitTemplate + diff) + pipe.close() + + response = "e" + while response == "e": + response = raw_input("Do you want to submit this change (y/e/n)? ") + if response == "e": + [handle, fileName] = tempfile.mkstemp() + tmpFile = os.fdopen(handle, "w+") + tmpFile.write(submitTemplate) + tmpFile.close() + editor = os.environ.get("EDITOR", "vi") + system(editor + " " + fileName) + tmpFile = open(fileName, "r") + submitTemplate = tmpFile.read() + tmpFile.close() + os.remove(fileName) + + if response == "y" or response == "yes": + if self.dryRun: + print submitTemplate + raw_input("Press return to continue...") + else: + pipe = os.popen("p4 submit -i", "w") + pipe.write(submitTemplate) + pipe.close() + else: + print "Not submitting!" + self.interactive = False + else: + fileName = "submit.txt" + file = open(fileName, "w+") + file.write(self.prepareLogMessage(template, logMessage)) + file.close() + print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) + + def run(self, args): + if self.reset: + self.firstTime = True + + if len(self.substFile) > 0: + for line in open(self.substFile, "r").readlines(): + tokens = line[:-1].split("=") + self.logSubstitutions[tokens[0]] = tokens[1] + + if len(self.master) == 0: + self.master = currentGitBranch() + if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)): + die("Detecting current git branch failed!") + + self.check() + self.configFile = gitdir + "/p4-git-sync.cfg" + self.config = shelve.open(self.configFile, writeback=True) + + if self.firstTime: + self.start() + + commits = self.config.get("commits", []) + + while len(commits) > 0: + self.firstTime = False + commit = commits[0] + commits = commits[1:] + self.config["commits"] = commits + self.apply(commit) + if not self.interactive: + break + + self.config.close() + + if len(commits) == 0: + if self.firstTime: + print "No changes found to apply between %s and current HEAD" % self.origin + else: + print "All changes applied!" + print "Deleting temporary p4-sync branch and going back to %s" % self.master + system("git checkout %s" % self.master) + system("git branch -D p4-sync") + print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." + system("p4 edit ... >/dev/null") + system("p4 revert ... >/dev/null") + os.remove(self.configFile) + + def printUsage(commands): print "usage: %s [options]" % sys.argv[0] print "" @@ -93,7 +313,8 @@ def printUsage(commands): commands = { "debug" : P4Debug(), - "clean-tags" : P4CleanTags() + "clean-tags" : P4CleanTags(), + "sync-to-perforce" : P4Sync() } if len(sys.argv[1:]) == 0: @@ -110,9 +331,25 @@ except KeyError: printUsage(commands.keys()) sys.exit(2) -parser = optparse.OptionParser("usage: %prog " + cmdName + " [options]", cmd.options, +options = cmd.options +cmd.gitdir = gitdir +options.append(optparse.make_option("--git-dir", dest="gitdir")) + +parser = optparse.OptionParser("usage: %prog " + cmdName + " [options]", options, description = cmd.description) (cmd, args) = parser.parse_args(sys.argv[2:], cmd); +gitdir = cmd.gitdir +if len(gitdir) == 0: + gitdir = ".git" + +if not isValidGitDir(gitdir): + if isValidGitDir(gitdir + "/.git"): + gitdir += "/.git" + else: + dir("fatal: cannot locate git repository at %s" % gitdir) + +os.environ["GIT_DIR"] = gitdir + cmd.run(args) -- cgit v1.2.3 From 83dce55af3c792134787bdc807f932dc8a67ea74 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 19 Mar 2007 22:26:36 +0100 Subject: Part of the code is copyright by Trolltech ASA. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 593bda822f..fa1b19fbbc 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -3,6 +3,8 @@ # git-p4.py -- A tool for bidirectional operation between a Perforce depot and git. # # Author: Simon Hausmann +# Copyright: 2007 Simon Hausmann +# 2007 Trolltech ASA # License: MIT # -- cgit v1.2.3 From 05140f342e1df7319dd3da2ef8157bfd5760fee6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Mar 2007 18:32:47 +0100 Subject: sync-to-perforce is now called submit and fixed the gitdir check a little bit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index fa1b19fbbc..aca1bcaab2 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -316,7 +316,7 @@ def printUsage(commands): commands = { "debug" : P4Debug(), "clean-tags" : P4CleanTags(), - "sync-to-perforce" : P4Sync() + "submit" : P4Sync() } if len(sys.argv[1:]) == 0: @@ -350,7 +350,7 @@ if not isValidGitDir(gitdir): if isValidGitDir(gitdir + "/.git"): gitdir += "/.git" else: - dir("fatal: cannot locate git repository at %s" % gitdir) + die("fatal: cannot locate git repository at %s" % gitdir) os.environ["GIT_DIR"] = gitdir -- cgit v1.2.3 From b984733c8048423537540fdca681ddc448b60fcc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Mar 2007 20:54:23 +0100 Subject: Completely untested "merge" of p4-fast-export.py into git-p4.py Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 590 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 582 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index aca1bcaab2..477238fa1c 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -9,7 +9,8 @@ # import optparse, sys, os, marshal, popen2, shelve -import tempfile +import tempfile, getopt, sha, os.path, time +from sets import Set; gitdir = os.environ.get("GIT_DIR", "") @@ -51,7 +52,11 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) -class P4Debug: +class Command: + def __init__(self): + self.usage = "usage: %prog [options]" + +class P4Debug(Command): def __init__(self): self.options = [ ] @@ -60,9 +65,11 @@ class P4Debug: def run(self, args): for output in p4CmdList(" ".join(args)): print output + return True -class P4CleanTags: +class P4CleanTags(Command): def __init__(self): + Command.__init__(self) self.options = [ # optparse.make_option("--branch", dest="branch", default="refs/heads/master") ] @@ -96,9 +103,11 @@ class P4CleanTags: print os.popen("git tag -d p4/%s" % rev).read() print "%s tags removed." % len(allTags) + return True -class P4Sync: +class P4Sync(Command): def __init__(self): + Command.__init__(self) self.options = [ optparse.make_option("--continue", action="store_false", dest="firstTime"), optparse.make_option("--origin", dest="origin"), @@ -304,6 +313,566 @@ class P4Sync: system("p4 revert ... >/dev/null") os.remove(self.configFile) + return True + +class GitSync(Command): + def __init__(self): + Command.__init__(self) + self.options = [ + optparse.make_option("--branch", dest="branch"), + optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"), + optparse.make_option("--changesfile", dest="changesFile"), + optparse.make_option("--silent", dest="silent", action="store_true"), + optparse.make_option("--known-branches", dest="knownBranches"), + optparse.make_option("--cache", dest="doCache", action="store_true"), + optparse.make_option("--command-cache", dest="commandCache", action="store_true") + ] + self.description = """Imports from Perforce into a git repository.\n + example: + //depot/my/project/ -- to import the current head + //depot/my/project/@all -- to import everything + //depot/my/project/@1,6 -- to import only from revision 1 to 6 + + (a ... is not needed in the path p4 specification, it's added implicitly)""" + + self.usage += " //depot/path[@revRange]" + + self.dataCache = False + self.commandCache = False + self.silent = False + self.knownBranches = Set() + self.createdBranches = Set() + self.committedChanges = Set() + self.branch = "master" + self.detectBranches = False + self.changesFile = "" + + def p4File(self, depotPath): + return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + + def extractFilesFromCommit(self, commit): + files = [] + fnum = 0 + while commit.has_key("depotFile%s" % fnum): + path = commit["depotFile%s" % fnum] + if not path.startswith(self.globalPrefix): + # if not self.silent: + # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.globalPrefix, change) + fnum = fnum + 1 + continue + + file = {} + file["path"] = path + file["rev"] = commit["rev%s" % fnum] + file["action"] = commit["action%s" % fnum] + file["type"] = commit["type%s" % fnum] + files.append(file) + fnum = fnum + 1 + return files + + def isSubPathOf(self, first, second): + if not first.startswith(second): + return False + if first == second: + return True + return first[len(second)] == "/" + + def branchesForCommit(self, files): + branches = Set() + + for file in files: + relativePath = file["path"][len(self.globalPrefix):] + # strip off the filename + relativePath = relativePath[0:relativePath.rfind("/")] + + # if len(branches) == 0: + # branches.add(relativePath) + # knownBranches.add(relativePath) + # continue + + ###### this needs more testing :) + knownBranch = False + for branch in branches: + if relativePath == branch: + knownBranch = True + break + # if relativePath.startswith(branch): + if self.isSubPathOf(relativePath, branch): + knownBranch = True + break + # if branch.startswith(relativePath): + if self.isSubPathOf(branch, relativePath): + branches.remove(branch) + break + + if knownBranch: + continue + + for branch in knownBranches: + #if relativePath.startswith(branch): + if self.isSubPathOf(relativePath, branch): + if len(branches) == 0: + relativePath = branch + else: + knownBranch = True + break + + if knownBranch: + continue + + branches.add(relativePath) + self.knownBranches.add(relativePath) + + return branches + + def findBranchParent(self, branchPrefix, files): + for file in files: + path = file["path"] + if not path.startswith(branchPrefix): + continue + action = file["action"] + if action != "integrate" and action != "branch": + continue + rev = file["rev"] + depotPath = path + "#" + rev + + log = p4CmdList("filelog \"%s\"" % depotPath) + if len(log) != 1: + print "eek! I got confused by the filelog of %s" % depotPath + sys.exit(1); + + log = log[0] + if log["action0"] != action: + print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) + sys.exit(1); + + branchAction = log["how0,0"] + # if branchAction == "branch into" or branchAction == "ignored": + # continue # ignore for branching + + if not branchAction.endswith(" from"): + continue # ignore for branching + # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) + # sys.exit(1); + + source = log["file0,0"] + if source.startswith(branchPrefix): + continue + + lastSourceRev = log["erev0,0"] + + sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) + if len(sourceLog) != 1: + print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) + sys.exit(1); + sourceLog = sourceLog[0] + + relPath = source[len(self.globalPrefix):] + # strip off the filename + relPath = relPath[0:relPath.rfind("/")] + + for branch in self.knownBranches: + if self.isSubPathOf(relPath, branch): + # print "determined parent branch branch %s due to change in file %s" % (branch, source) + return branch + # else: + # print "%s is not a subpath of branch %s" % (relPath, branch) + + return "" + + def commit(self, details, files, branch, branchPrefix, parent, merged = ""): + epoch = details["time"] + author = details["user"] + + self.gitStream.write("commit %s\n" % branch) + # gitStream.write("mark :%s\n" % details["change"]) + self.committedChanges.add(int(details["change"])) + committer = "" + if author in self.users: + committer = "%s %s %s" % (self.users[author], epoch, tz) + else: + committer = "%s %s %s" % (author, epoch, tz) + + self.gitStream.write("committer %s\n" % committer) + + self.gitStream.write("data < 0: + self.gitStream.write("from %s\n" % parent) + + if len(merged) > 0: + self.gitStream.write("merge %s\n" % merged) + + for file in files: + path = file["path"] + if not path.startswith(branchPrefix): + # if not silent: + # print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) + continue + rev = file["rev"] + depotPath = path + "#" + rev + relPath = path[len(branchPrefix):] + action = file["action"] + + if file["type"] == "apple": + print "\nfile %s is a strange apple file that forks. Ignoring!" % path + continue + + if action == "delete": + self.gitStream.write("D %s\n" % relPath) + else: + mode = 644 + if file["type"].startswith("x"): + mode = 755 + + data = self.p4File(depotPath) + + self.gitStream.write("M %s inline %s\n" % (mode, relPath)) + self.gitStream.write("data %s\n" % len(data)) + self.gitStream.write(data) + self.gitStream.write("\n") + + self.gitStream.write("\n") + + self.lastChange = int(details["change"]) + + def extractFilesInCommitToBranch(self, files, branchPrefix): + newFiles = [] + + for file in files: + path = file["path"] + if path.startswith(branchPrefix): + newFiles.append(file) + + return newFiles + + def findBranchSourceHeuristic(self, files, branch, branchPrefix): + for file in files: + action = file["action"] + if action != "integrate" and action != "branch": + continue + path = file["path"] + rev = file["rev"] + depotPath = path + "#" + rev + + log = p4CmdList("filelog \"%s\"" % depotPath) + if len(log) != 1: + print "eek! I got confused by the filelog of %s" % depotPath + sys.exit(1); + + log = log[0] + if log["action0"] != action: + print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) + sys.exit(1); + + branchAction = log["how0,0"] + + if not branchAction.endswith(" from"): + continue # ignore for branching + # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) + # sys.exit(1); + + source = log["file0,0"] + if source.startswith(branchPrefix): + continue + + lastSourceRev = log["erev0,0"] + + sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) + if len(sourceLog) != 1: + print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) + sys.exit(1); + sourceLog = sourceLog[0] + + relPath = source[len(self.globalPrefix):] + # strip off the filename + relPath = relPath[0:relPath.rfind("/")] + + for candidate in self.knownBranches: + if self.isSubPathOf(relPath, candidate) and candidate != branch: + return candidate + + return "" + + def changeIsBranchMerge(self, sourceBranch, destinationBranch, change): + sourceFiles = {} + for file in p4CmdList("files %s...@%s" % (self.globalPrefix + sourceBranch + "/", change)): + if file["action"] == "delete": + continue + sourceFiles[file["depotFile"]] = file + + destinationFiles = {} + for file in p4CmdList("files %s...@%s" % (self.globalPrefix + destinationBranch + "/", change)): + destinationFiles[file["depotFile"]] = file + + for fileName in sourceFiles.keys(): + integrations = [] + deleted = False + integrationCount = 0 + for integration in p4CmdList("integrated \"%s\"" % fileName): + toFile = integration["fromFile"] # yes, it's true, it's fromFile + if not toFile in destinationFiles: + continue + destFile = destinationFiles[toFile] + if destFile["action"] == "delete": + # print "file %s has been deleted in %s" % (fileName, toFile) + deleted = True + break + integrationCount += 1 + if integration["how"] == "branch from": + continue + + if int(integration["change"]) == change: + integrations.append(integration) + continue + if int(integration["change"]) > change: + continue + + destRev = int(destFile["rev"]) + + startRev = integration["startFromRev"][1:] + if startRev == "none": + startRev = 0 + else: + startRev = int(startRev) + + endRev = integration["endFromRev"][1:] + if endRev == "none": + endRev = 0 + else: + endRev = int(endRev) + + initialBranch = (destRev == 1 and integration["how"] != "branch into") + inRange = (destRev >= startRev and destRev <= endRev) + newer = (destRev > startRev and destRev > endRev) + + if initialBranch or inRange or newer: + integrations.append(integration) + + if deleted: + continue + + if len(integrations) == 0 and integrationCount > 1: + print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) + return False + + return True + + def getUserMap(self): + self.users = {} + + for output in p4CmdList("users"): + if not output.has_key("User"): + continue + self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" + + def run(self, args): + self.branch = "refs/heads/" + self.branch + self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() + if len(self.globalPrefix) != 0: + self.globalPrefix = self.globalPrefix[:-1] + + if len(args) == 0 and len(self.globalPrefix) != 0: + if not self.silent: + print "[using previously specified depot path %s]" % self.globalPrefix + elif len(args) != 1: + return False + else: + if len(self.globalPrefix) != 0 and self.globalPrefix != args[0]: + print "previous import used depot path %s and now %s was specified. this doesn't work!" % (self.globalPrefix, args[0]) + sys.exit(1) + self.globalPrefix = args[0] + + self.changeRange = "" + self.revision = "" + self.users = {} + self.initialParent = "" + self.lastChange = 0 + self.initialTag = "" + + if self.globalPrefix.find("@") != -1: + atIdx = self.globalPrefix.index("@") + self.changeRange = self.globalPrefix[atIdx:] + if self.changeRange == "@all": + self.changeRange = "" + elif self.changeRange.find(",") == -1: + self.revision = self.changeRange + self.changeRange = "" + self.globalPrefix = self.globalPrefix[0:atIdx] + elif self.globalPrefix.find("#") != -1: + hashIdx = self.globalPrefix.index("#") + self.revision = self.globalPrefix[hashIdx:] + self.globalPrefix = self.globalPrefix[0:hashIdx] + elif len(self.previousDepotPath) == 0: + self.revision = "#head" + + if self.globalPrefix.endswith("..."): + self.globalPrefix = self.globalPrefix[:-3] + + if not self.globalPrefix.endswith("/"): + self.globalPrefix += "/" + + self.getUserMap() + + if len(self.changeRange) == 0: + try: + sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % self.branch) + output = sout.read() + if output.endswith("\n"): + output = output[:-1] + tagIdx = output.index(" tags/p4/") + caretIdx = output.find("^") + endPos = len(output) + if caretIdx != -1: + endPos = caretIdx + self.rev = int(output[tagIdx + 9 : endPos]) + 1 + self.changeRange = "@%s,#head" % self.rev + self.initialParent = os.popen("git-rev-parse %s" % self.branch).read()[:-1] + self.initialTag = "p4/%s" % (int(self.rev) - 1) + except: + pass + + tz = - time.timezone / 36 + tzsign = ("%s" % tz)[0] + if tzsign != '+' and tzsign != '-': + tz = "+" + ("%s" % tz) + + self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git-fast-import") + + if len(self.revision) > 0: + print "Doing initial import of %s from revision %s" % (self.globalPrefix, self.revision) + + details = { "user" : "git perforce import user", "time" : int(time.time()) } + details["desc"] = "Initial import of %s from the state at revision %s" % (self.globalPrefix, self.revision) + details["change"] = self.revision + newestRevision = 0 + + fileCnt = 0 + for info in p4CmdList("files %s...%s" % (self.globalPrefix, self.revision)): + change = int(info["change"]) + if change > newestRevision: + newestRevision = change + + if info["action"] == "delete": + continue + + for prop in [ "depotFile", "rev", "action", "type" ]: + details["%s%s" % (prop, fileCnt)] = info[prop] + + fileCnt = fileCnt + 1 + + details["change"] = newestRevision + + try: + self.commit(details, self.extractFilesFromCommit(details), self.branch, self.globalPrefix) + except: + print self.gitError.read() + + else: + changes = [] + + if len(changesFile) > 0: + output = open(self.changesFile).readlines() + changeSet = Set() + for line in output: + changeSet.add(int(line)) + + for change in changeSet: + changes.append(change) + + changes.sort() + else: + output = os.popen("p4 changes %s...%s" % (self.globalPrefix, self.changeRange)).readlines() + + for line in output: + changeNum = line.split(" ")[1] + changes.append(changeNum) + + changes.reverse() + + if len(changes) == 0: + if not silent: + print "no changes to import!" + sys.exit(1) + + cnt = 1 + for change in changes: + description = p4Cmd("describe %s" % change) + + if not silent: + sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() + cnt = cnt + 1 + + try: + files = self.extractFilesFromCommit(description) + if self.detectBranches: + for branch in self.branchesForCommit(files): + self.knownBranches.add(branch) + branchPrefix = self.globalPrefix + branch + "/" + + filesForCommit = self.extractFilesInCommitToBranch(files, branchPrefix) + + merged = "" + parent = "" + ########### remove cnt!!! + if branch not in self.createdBranches and cnt > 2: + self.createdBranches.add(branch) + parent = self.findBranchParent(branchPrefix, files) + if parent == branch: + parent = "" + # elif len(parent) > 0: + # print "%s branched off of %s" % (branch, parent) + + if len(parent) == 0: + merged = self.findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) + if len(merged) > 0: + print "change %s could be a merge from %s into %s" % (description["change"], merged, branch) + if not self.changeIsBranchMerge(merged, branch, int(description["change"])): + merged = "" + + branch = "refs/heads/" + branch + if len(parent) > 0: + parent = "refs/heads/" + parent + if len(merged) > 0: + merged = "refs/heads/" + merged + self.commit(description, files, branch, branchPrefix, parent, merged) + else: + self.commit(description, files, branch, globalPrefix, initialParent) + self.initialParent = "" + except IOError: + print self.gitError.read() + sys.exit(1) + + if not self.silent: + print "" + + self.gitStream.write("reset refs/tags/p4/%s\n" % self.lastChange) + self.gitStream.write("from %s\n\n" % self.branch); + + + self.gitStream.close() + self.gitOutput.close() + self.gitError.close() + + os.popen("git-repo-config p4.depotpath %s" % self.globalPrefix).read() + if len(self.initialTag) > 0: + os.popen("git tag -d %s" % self.initialTag).read() + + return True + +class HelpFormatter(optparse.IndentedHelpFormatter): + def __init__(self): + optparse.IndentedHelpFormatter.__init__(self) + + def format_description(self, description): + if description: + return description + "\n" + else: + return "" def printUsage(commands): print "usage: %s [options]" % sys.argv[0] @@ -316,7 +885,8 @@ def printUsage(commands): commands = { "debug" : P4Debug(), "clean-tags" : P4CleanTags(), - "submit" : P4Sync() + "submit" : P4Sync(), + "sync" : GitSync() } if len(sys.argv[1:]) == 0: @@ -337,8 +907,10 @@ options = cmd.options cmd.gitdir = gitdir options.append(optparse.make_option("--git-dir", dest="gitdir")) -parser = optparse.OptionParser("usage: %prog " + cmdName + " [options]", options, - description = cmd.description) +parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), + options, + description = cmd.description, + formatter = HelpFormatter()) (cmd, args) = parser.parse_args(sys.argv[2:], cmd); @@ -354,4 +926,6 @@ if not isValidGitDir(gitdir): os.environ["GIT_DIR"] = gitdir -cmd.run(args) +if not cmd.run(args): + parser.print_help() + -- cgit v1.2.3 From 0828ab140360c4c831e112f8d244923a7f032e74 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Mar 2007 20:59:30 +0100 Subject: Added missing "self"s to make the script evaluate correctly. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 477238fa1c..8cb63f9540 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -489,9 +489,9 @@ class GitSync(Command): self.committedChanges.add(int(details["change"])) committer = "" if author in self.users: - committer = "%s %s %s" % (self.users[author], epoch, tz) + committer = "%s %s %s" % (self.users[author], epoch, self.tz) else: - committer = "%s %s %s" % (author, epoch, tz) + committer = "%s %s %s" % (author, epoch, self.tz) self.gitStream.write("committer %s\n" % committer) @@ -735,10 +735,10 @@ class GitSync(Command): except: pass - tz = - time.timezone / 36 - tzsign = ("%s" % tz)[0] + self.tz = - time.timezone / 36 + tzsign = ("%s" % self.tz)[0] if tzsign != '+' and tzsign != '-': - tz = "+" + ("%s" % tz) + self.tz = "+" + ("%s" % self.tz) self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git-fast-import") @@ -774,7 +774,7 @@ class GitSync(Command): else: changes = [] - if len(changesFile) > 0: + if len(self.changesFile) > 0: output = open(self.changesFile).readlines() changeSet = Set() for line in output: @@ -794,7 +794,7 @@ class GitSync(Command): changes.reverse() if len(changes) == 0: - if not silent: + if not self.silent: print "no changes to import!" sys.exit(1) @@ -802,7 +802,7 @@ class GitSync(Command): for change in changes: description = p4Cmd("describe %s" % change) - if not silent: + if not self.silent: sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) sys.stdout.flush() cnt = cnt + 1 @@ -841,7 +841,7 @@ class GitSync(Command): merged = "refs/heads/" + merged self.commit(description, files, branch, branchPrefix, parent, merged) else: - self.commit(description, files, branch, globalPrefix, initialParent) + self.commit(description, files, self.branch, self.globalPrefix, self.initialParent) self.initialParent = "" except IOError: print self.gitError.read() -- cgit v1.2.3 From c715706b15426c7b106677868ce48e4bd78b94d6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Mar 2007 21:13:49 +0100 Subject: Fixed the initial version import by getting the file index correct by correctly skipping deleted files. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 8cb63f9540..54ddf73dac 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -480,7 +480,7 @@ class GitSync(Command): return "" - def commit(self, details, files, branch, branchPrefix, parent, merged = ""): + def commit(self, details, files, branch, branchPrefix, parent = "", merged = ""): epoch = details["time"] author = details["user"] @@ -757,6 +757,7 @@ class GitSync(Command): newestRevision = change if info["action"] == "delete": + fileCnt = fileCnt + 1 continue for prop in [ "depotFile", "rev", "action", "type" ]: @@ -768,7 +769,7 @@ class GitSync(Command): try: self.commit(details, self.extractFilesFromCommit(details), self.branch, self.globalPrefix) - except: + except IOError: print self.gitError.read() else: -- cgit v1.2.3 From c5fdcbcc20ebf0716fd17e3ac4f73baacf38a699 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Mar 2007 22:09:27 +0100 Subject: Removed p4-fast-export and p4-git-sync as they've been integrated into git-p4 now. Signed-off-by: Simon Hausmann --- contrib/fast-import/p4-fast-export.py | 632 ---------------------------------- contrib/fast-import/p4-git-sync.py | 296 ---------------- 2 files changed, 928 deletions(-) delete mode 100755 contrib/fast-import/p4-fast-export.py delete mode 100755 contrib/fast-import/p4-git-sync.py (limited to 'contrib') diff --git a/contrib/fast-import/p4-fast-export.py b/contrib/fast-import/p4-fast-export.py deleted file mode 100755 index 9adb88fade..0000000000 --- a/contrib/fast-import/p4-fast-export.py +++ /dev/null @@ -1,632 +0,0 @@ -#!/usr/bin/env python -# -# p4-fast-export.py -# -# Author: Simon Hausmann -# License: MIT -# -# TODO: -# - support integrations (at least p4i) -# - support p4 submit (hah!) -# - emulate p4's delete behavior: if a directory becomes empty delete it. continue -# with parent dir until non-empty dir is found. -# -import os, string, sys, time, os.path -import marshal, popen2, getopt, sha -from sets import Set; - -dataCache = False -commandCache = False - -silent = False -knownBranches = Set() -createdBranches = Set() -committedChanges = Set() -branch = "refs/heads/master" -globalPrefix = previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() -detectBranches = False -changesFile = "" -if len(globalPrefix) != 0: - globalPrefix = globalPrefix[:-1] - -try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "branch=", "detect-branches", "changesfile=", "silent", "known-branches=", - "cache", "command-cache" ]) -except getopt.GetoptError: - print "fixme, syntax error" - sys.exit(1) - -for o, a in opts: - if o == "--branch": - branch = "refs/heads/" + a - elif o == "--detect-branches": - detectBranches = True - elif o == "--changesfile": - changesFile = a - elif o == "--silent": - silent= True - elif o == "--known-branches": - for branch in open(a).readlines(): - knownBranches.add(branch[:-1]) - elif o == "--cache": - dataCache = True - commandCache = True - elif o == "--command-cache": - commandCache = True - -if len(args) == 0 and len(globalPrefix) != 0: - if not silent: - print "[using previously specified depot path %s]" % globalPrefix -elif len(args) != 1: - print "usage: %s //depot/path[@revRange]" % sys.argv[0] - print "\n example:" - print " %s //depot/my/project/ -- to import the current head" - print " %s //depot/my/project/@all -- to import everything" - print " %s //depot/my/project/@1,6 -- to import only from revision 1 to 6" - print "" - print " (a ... is not needed in the path p4 specification, it's added implicitly)" - print "" - sys.exit(1) -else: - if len(globalPrefix) != 0 and globalPrefix != args[0]: - print "previous import used depot path %s and now %s was specified. this doesn't work!" % (globalPrefix, args[0]) - sys.exit(1) - globalPrefix = args[0] - -changeRange = "" -revision = "" -users = {} -initialParent = "" -lastChange = 0 -initialTag = "" - -if globalPrefix.find("@") != -1: - atIdx = globalPrefix.index("@") - changeRange = globalPrefix[atIdx:] - if changeRange == "@all": - changeRange = "" - elif changeRange.find(",") == -1: - revision = changeRange - changeRange = "" - globalPrefix = globalPrefix[0:atIdx] -elif globalPrefix.find("#") != -1: - hashIdx = globalPrefix.index("#") - revision = globalPrefix[hashIdx:] - globalPrefix = globalPrefix[0:hashIdx] -elif len(previousDepotPath) == 0: - revision = "#head" - -if globalPrefix.endswith("..."): - globalPrefix = globalPrefix[:-3] - -if not globalPrefix.endswith("/"): - globalPrefix += "/" - -def p4File(depotPath): - cacheKey = "/tmp/p4cache/data-" + sha.new(depotPath).hexdigest() - - data = 0 - try: - if not dataCache: - raise - data = open(cacheKey, "rb").read() - except: - data = os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() - if dataCache: - open(cacheKey, "wb").write(data) - - return data - -def p4CmdList(cmd): - fullCmd = "p4 -G %s" % cmd; - - cacheKey = sha.new(fullCmd).hexdigest() - cacheKey = "/tmp/p4cache/cmd-" + cacheKey - - cached = True - pipe = 0 - try: - if not commandCache: - raise - pipe = open(cacheKey, "rb") - except: - cached = False - pipe = os.popen(fullCmd, "rb") - - result = [] - try: - while True: - entry = marshal.load(pipe) - result.append(entry) - except EOFError: - pass - pipe.close() - - if not cached and commandCache: - pipe = open(cacheKey, "wb") - for r in result: - marshal.dump(r, pipe) - pipe.close() - - return result - -def p4Cmd(cmd): - list = p4CmdList(cmd) - result = {} - for entry in list: - result.update(entry) - return result; - -def extractFilesFromCommit(commit): - files = [] - fnum = 0 - while commit.has_key("depotFile%s" % fnum): - path = commit["depotFile%s" % fnum] - if not path.startswith(globalPrefix): -# if not silent: -# print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, globalPrefix, change) - fnum = fnum + 1 - continue - - file = {} - file["path"] = path - file["rev"] = commit["rev%s" % fnum] - file["action"] = commit["action%s" % fnum] - file["type"] = commit["type%s" % fnum] - files.append(file) - fnum = fnum + 1 - return files - -def isSubPathOf(first, second): - if not first.startswith(second): - return False - if first == second: - return True - return first[len(second)] == "/" - -def branchesForCommit(files): - global knownBranches - branches = Set() - - for file in files: - relativePath = file["path"][len(globalPrefix):] - # strip off the filename - relativePath = relativePath[0:relativePath.rfind("/")] - -# if len(branches) == 0: -# branches.add(relativePath) -# knownBranches.add(relativePath) -# continue - - ###### this needs more testing :) - knownBranch = False - for branch in branches: - if relativePath == branch: - knownBranch = True - break -# if relativePath.startswith(branch): - if isSubPathOf(relativePath, branch): - knownBranch = True - break -# if branch.startswith(relativePath): - if isSubPathOf(branch, relativePath): - branches.remove(branch) - break - - if knownBranch: - continue - - for branch in knownBranches: - #if relativePath.startswith(branch): - if isSubPathOf(relativePath, branch): - if len(branches) == 0: - relativePath = branch - else: - knownBranch = True - break - - if knownBranch: - continue - - branches.add(relativePath) - knownBranches.add(relativePath) - - return branches - -def findBranchParent(branchPrefix, files): - for file in files: - path = file["path"] - if not path.startswith(branchPrefix): - continue - action = file["action"] - if action != "integrate" and action != "branch": - continue - rev = file["rev"] - depotPath = path + "#" + rev - - log = p4CmdList("filelog \"%s\"" % depotPath) - if len(log) != 1: - print "eek! I got confused by the filelog of %s" % depotPath - sys.exit(1); - - log = log[0] - if log["action0"] != action: - print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) - sys.exit(1); - - branchAction = log["how0,0"] -# if branchAction == "branch into" or branchAction == "ignored": -# continue # ignore for branching - - if not branchAction.endswith(" from"): - continue # ignore for branching -# print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) -# sys.exit(1); - - source = log["file0,0"] - if source.startswith(branchPrefix): - continue - - lastSourceRev = log["erev0,0"] - - sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) - if len(sourceLog) != 1: - print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) - sys.exit(1); - sourceLog = sourceLog[0] - - relPath = source[len(globalPrefix):] - # strip off the filename - relPath = relPath[0:relPath.rfind("/")] - - for branch in knownBranches: - if isSubPathOf(relPath, branch): -# print "determined parent branch branch %s due to change in file %s" % (branch, source) - return branch -# else: -# print "%s is not a subpath of branch %s" % (relPath, branch) - - return "" - -def commit(details, files, branch, branchPrefix, parent, merged = ""): - global users - global lastChange - global committedChanges - - epoch = details["time"] - author = details["user"] - - gitStream.write("commit %s\n" % branch) -# gitStream.write("mark :%s\n" % details["change"]) - committedChanges.add(int(details["change"])) - committer = "" - if author in users: - committer = "%s %s %s" % (users[author], epoch, tz) - else: - committer = "%s %s %s" % (author, epoch, tz) - - gitStream.write("committer %s\n" % committer) - - gitStream.write("data < 0: - gitStream.write("from %s\n" % parent) - - if len(merged) > 0: - gitStream.write("merge %s\n" % merged) - - for file in files: - path = file["path"] - if not path.startswith(branchPrefix): -# if not silent: -# print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) - continue - rev = file["rev"] - depotPath = path + "#" + rev - relPath = path[len(branchPrefix):] - action = file["action"] - - if file["type"] == "apple": - print "\nfile %s is a strange apple file that forks. Ignoring!" % path - continue - - if action == "delete": - gitStream.write("D %s\n" % relPath) - else: - mode = 644 - if file["type"].startswith("x"): - mode = 755 - - data = p4File(depotPath) - - gitStream.write("M %s inline %s\n" % (mode, relPath)) - gitStream.write("data %s\n" % len(data)) - gitStream.write(data) - gitStream.write("\n") - - gitStream.write("\n") - - lastChange = int(details["change"]) - -def extractFilesInCommitToBranch(files, branchPrefix): - newFiles = [] - - for file in files: - path = file["path"] - if path.startswith(branchPrefix): - newFiles.append(file) - - return newFiles - -def findBranchSourceHeuristic(files, branch, branchPrefix): - for file in files: - action = file["action"] - if action != "integrate" and action != "branch": - continue - path = file["path"] - rev = file["rev"] - depotPath = path + "#" + rev - - log = p4CmdList("filelog \"%s\"" % depotPath) - if len(log) != 1: - print "eek! I got confused by the filelog of %s" % depotPath - sys.exit(1); - - log = log[0] - if log["action0"] != action: - print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) - sys.exit(1); - - branchAction = log["how0,0"] - - if not branchAction.endswith(" from"): - continue # ignore for branching -# print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) -# sys.exit(1); - - source = log["file0,0"] - if source.startswith(branchPrefix): - continue - - lastSourceRev = log["erev0,0"] - - sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) - if len(sourceLog) != 1: - print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) - sys.exit(1); - sourceLog = sourceLog[0] - - relPath = source[len(globalPrefix):] - # strip off the filename - relPath = relPath[0:relPath.rfind("/")] - - for candidate in knownBranches: - if isSubPathOf(relPath, candidate) and candidate != branch: - return candidate - - return "" - -def changeIsBranchMerge(sourceBranch, destinationBranch, change): - sourceFiles = {} - for file in p4CmdList("files %s...@%s" % (globalPrefix + sourceBranch + "/", change)): - if file["action"] == "delete": - continue - sourceFiles[file["depotFile"]] = file - - destinationFiles = {} - for file in p4CmdList("files %s...@%s" % (globalPrefix + destinationBranch + "/", change)): - destinationFiles[file["depotFile"]] = file - - for fileName in sourceFiles.keys(): - integrations = [] - deleted = False - integrationCount = 0 - for integration in p4CmdList("integrated \"%s\"" % fileName): - toFile = integration["fromFile"] # yes, it's true, it's fromFile - if not toFile in destinationFiles: - continue - destFile = destinationFiles[toFile] - if destFile["action"] == "delete": -# print "file %s has been deleted in %s" % (fileName, toFile) - deleted = True - break - integrationCount += 1 - if integration["how"] == "branch from": - continue - - if int(integration["change"]) == change: - integrations.append(integration) - continue - if int(integration["change"]) > change: - continue - - destRev = int(destFile["rev"]) - - startRev = integration["startFromRev"][1:] - if startRev == "none": - startRev = 0 - else: - startRev = int(startRev) - - endRev = integration["endFromRev"][1:] - if endRev == "none": - endRev = 0 - else: - endRev = int(endRev) - - initialBranch = (destRev == 1 and integration["how"] != "branch into") - inRange = (destRev >= startRev and destRev <= endRev) - newer = (destRev > startRev and destRev > endRev) - - if initialBranch or inRange or newer: - integrations.append(integration) - - if deleted: - continue - - if len(integrations) == 0 and integrationCount > 1: - print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) - return False - - return True - -def getUserMap(): - users = {} - - for output in p4CmdList("users"): - if not output.has_key("User"): - continue - users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" - return users - -users = getUserMap() - -if len(changeRange) == 0: - try: - sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) - output = sout.read() - if output.endswith("\n"): - output = output[:-1] - tagIdx = output.index(" tags/p4/") - caretIdx = output.find("^") - endPos = len(output) - if caretIdx != -1: - endPos = caretIdx - rev = int(output[tagIdx + 9 : endPos]) + 1 - changeRange = "@%s,#head" % rev - initialParent = os.popen("git-rev-parse %s" % branch).read()[:-1] - initialTag = "p4/%s" % (int(rev) - 1) - except: - pass - -tz = - time.timezone / 36 -tzsign = ("%s" % tz)[0] -if tzsign != '+' and tzsign != '-': - tz = "+" + ("%s" % tz) - -gitOutput, gitStream, gitError = popen2.popen3("git-fast-import") - -if len(revision) > 0: - print "Doing initial import of %s from revision %s" % (globalPrefix, revision) - - details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = "Initial import of %s from the state at revision %s" % (globalPrefix, revision) - details["change"] = revision - newestRevision = 0 - - fileCnt = 0 - for info in p4CmdList("files %s...%s" % (globalPrefix, revision)): - change = int(info["change"]) - if change > newestRevision: - newestRevision = change - - if info["action"] == "delete": - continue - - for prop in [ "depotFile", "rev", "action", "type" ]: - details["%s%s" % (prop, fileCnt)] = info[prop] - - fileCnt = fileCnt + 1 - - details["change"] = newestRevision - - try: - commit(details, extractFilesFromCommit(details), branch, globalPrefix) - except: - print gitError.read() - -else: - changes = [] - - if len(changesFile) > 0: - output = open(changesFile).readlines() - changeSet = Set() - for line in output: - changeSet.add(int(line)) - - for change in changeSet: - changes.append(change) - - changes.sort() - else: - output = os.popen("p4 changes %s...%s" % (globalPrefix, changeRange)).readlines() - - for line in output: - changeNum = line.split(" ")[1] - changes.append(changeNum) - - changes.reverse() - - if len(changes) == 0: - if not silent: - print "no changes to import!" - sys.exit(1) - - cnt = 1 - for change in changes: - description = p4Cmd("describe %s" % change) - - if not silent: - sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) - sys.stdout.flush() - cnt = cnt + 1 - - try: - files = extractFilesFromCommit(description) - if detectBranches: - for branch in branchesForCommit(files): - knownBranches.add(branch) - branchPrefix = globalPrefix + branch + "/" - - filesForCommit = extractFilesInCommitToBranch(files, branchPrefix) - - merged = "" - parent = "" - ########### remove cnt!!! - if branch not in createdBranches and cnt > 2: - createdBranches.add(branch) - parent = findBranchParent(branchPrefix, files) - if parent == branch: - parent = "" - # elif len(parent) > 0: - # print "%s branched off of %s" % (branch, parent) - - if len(parent) == 0: - merged = findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) - if len(merged) > 0: - print "change %s could be a merge from %s into %s" % (description["change"], merged, branch) - if not changeIsBranchMerge(merged, branch, int(description["change"])): - merged = "" - - branch = "refs/heads/" + branch - if len(parent) > 0: - parent = "refs/heads/" + parent - if len(merged) > 0: - merged = "refs/heads/" + merged - commit(description, files, branch, branchPrefix, parent, merged) - else: - commit(description, files, branch, globalPrefix, initialParent) - initialParent = "" - except IOError: - print gitError.read() - sys.exit(1) - -if not silent: - print "" - -gitStream.write("reset refs/tags/p4/%s\n" % lastChange) -gitStream.write("from %s\n\n" % branch); - - -gitStream.close() -gitOutput.close() -gitError.close() - -os.popen("git-repo-config p4.depotpath %s" % globalPrefix).read() -if len(initialTag) > 0: - os.popen("git tag -d %s" % initialTag).read() - -sys.exit(0) diff --git a/contrib/fast-import/p4-git-sync.py b/contrib/fast-import/p4-git-sync.py deleted file mode 100755 index 5a3bf90485..0000000000 --- a/contrib/fast-import/p4-git-sync.py +++ /dev/null @@ -1,296 +0,0 @@ -#!/usr/bin/env python -# -# p4-git-sync.py -# -# Author: Simon Hausmann -# Copyright: 2007 Simon Hausmann -# 2007 Trolltech ASA -# License: MIT -# - -import os, string, shelve, stat -import getopt, sys, marshal, tempfile - -def p4CmdList(cmd): - cmd = "p4 -G %s" % cmd - pipe = os.popen(cmd, "rb") - - result = [] - try: - while True: - entry = marshal.load(pipe) - result.append(entry) - except EOFError: - pass - pipe.close() - - return result - -def p4Cmd(cmd): - list = p4CmdList(cmd) - result = {} - for entry in list: - result.update(entry) - return result; - -def die(msg): - sys.stderr.write(msg + "\n") - sys.exit(1) - -def tryGitDir(path): - if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): - return True; - return False - -try: - opts, args = getopt.getopt(sys.argv[1:], "", [ "continue", "git-dir=", "origin=", "reset", "master=", - "submit-log-subst=", "log-substitutions=", "noninteractive", - "dry-run" ]) -except getopt.GetoptError: - print "fixme, syntax error" - sys.exit(1) - -logSubstitutions = {} -logSubstitutions[""] = "%log%" -logSubstitutions["\tDetails:"] = "\tDetails: %log%" -gitdir = os.environ.get("GIT_DIR", "") -origin = "origin" -master = "" -firstTime = True -reset = False -interactive = True -dryRun = False - -for o, a in opts: - if o == "--git-dir": - gitdir = a - elif o == "--origin": - origin = a - elif o == "--master": - master = a - elif o == "--continue": - firstTime = False - elif o == "--reset": - reset = True - firstTime = True - elif o == "--submit-log-subst": - key = a.split("%")[0] - value = a.split("%")[1] - logSubstitutions[key] = value - elif o == "--log-substitutions": - for line in open(a, "r").readlines(): - tokens = line[:-1].split("=") - logSubstitutions[tokens[0]] = tokens[1] - elif o == "--noninteractive": - interactive = False - elif o == "--dry-run": - dryRun = True - -if len(gitdir) == 0: - gitdir = ".git" -else: - os.environ["GIT_DIR"] = gitdir - -if not tryGitDir(gitdir): - if tryGitDir(gitdir + "/.git"): - gitdir += "/.git" - os.environ["GIT_DIR"] = gitdir - else: - die("fatal: %s seems not to be a git repository." % gitdir) - - -configFile = gitdir + "/p4-git-sync.cfg" - -origin = "origin" -if len(args) == 1: - origin = args[0] - -if len(master) == 0: - sys.stdout.write("Auto-detecting current branch: ") - master = os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] - if len(master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, master)): - die("\nFailed to detect current branch! Aborting!"); - sys.stdout.write("%s\n" % master) - -def system(cmd): - if os.system(cmd) != 0: - die("command failed: %s" % cmd) - -def check(): - if len(p4CmdList("opened ...")) > 0: - die("You have files opened with perforce! Close them before starting the sync.") - -def start(config): - if len(config) > 0 and not reset: - die("Cannot start sync. Previous sync config found at %s" % configFile) - - #if len(os.popen("git-update-index --refresh").read()) > 0: - # die("Your working tree is not clean. Check with git status!") - - commits = [] - for line in os.popen("git-rev-list --no-merges %s..%s" % (origin, master)).readlines(): - commits.append(line[:-1]) - commits.reverse() - - config["commits"] = commits - - print "Creating temporary p4-sync branch from %s ..." % origin - system("git checkout -f -b p4-sync %s" % origin) - -# print "Cleaning index..." -# system("git checkout -f") - -def prepareLogMessage(template, message): - result = "" - - for line in template.split("\n"): - if line.startswith("#"): - result += line + "\n" - continue - - substituted = False - for key in logSubstitutions.keys(): - if line.find(key) != -1: - value = logSubstitutions[key] - value = value.replace("%log%", message) - if value != "@remove@": - result += line.replace(key, value) + "\n" - substituted = True - break - - if not substituted: - result += line + "\n" - - return result - -def apply(id): - global interactive - print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) - diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() - filesToAdd = set() - filesToDelete = set() - for line in diff: - modifier = line[0] - path = line[1:].strip() - if modifier == "M": - system("p4 edit %s" % path) - elif modifier == "A": - filesToAdd.add(path) - if path in filesToDelete: - filesToDelete.remove(path) - elif modifier == "D": - filesToDelete.add(path) - if path in filesToAdd: - filesToAdd.remove(path) - else: - die("unknown modifier %s for %s" % (modifier, path)) - - system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") - system("git cherry-pick --no-commit \"%s\"" % id) - #system("git format-patch --stdout -k \"%s^\"..\"%s\" | git-am -k" % (id, id)) - #system("git branch -D tmp") - #system("git checkout -f -b tmp \"%s^\"" % id) - - for f in filesToAdd: - system("p4 add %s" % f) - for f in filesToDelete: - system("p4 revert %s" % f) - system("p4 delete %s" % f) - - logMessage = "" - foundTitle = False - for log in os.popen("git-cat-file commit %s" % id).readlines(): - if not foundTitle: - if len(log) == 1: - foundTitle = 1 - continue - - if len(logMessage) > 0: - logMessage += "\t" - logMessage += log - - template = os.popen("p4 change -o").read() - - if interactive: - submitTemplate = prepareLogMessage(template, logMessage) - diff = os.popen("p4 diff -du ...").read() - - for newFile in filesToAdd: - diff += "==== new file ====\n" - diff += "--- /dev/null\n" - diff += "+++ %s\n" % newFile - f = open(newFile, "r") - for line in f.readlines(): - diff += "+" + line - f.close() - - pipe = os.popen("less", "w") - pipe.write(submitTemplate + diff) - pipe.close() - - response = "e" - while response == "e": - response = raw_input("Do you want to submit this change (y/e/n)? ") - if response == "e": - [handle, fileName] = tempfile.mkstemp() - tmpFile = os.fdopen(handle, "w+") - tmpFile.write(submitTemplate) - tmpFile.close() - editor = os.environ.get("EDITOR", "vi") - system(editor + " " + fileName) - tmpFile = open(fileName, "r") - submitTemplate = tmpFile.read() - tmpFile.close() - os.remove(fileName) - - if response == "y" or response == "yes": - if dryRun: - print submitTemplate - raw_input("Press return to continue...") - else: - pipe = os.popen("p4 submit -i", "w") - pipe.write(submitTemplate) - pipe.close() - else: - print "Not submitting!" - interactive = False - else: - fileName = "submit.txt" - file = open(fileName, "w+") - file.write(prepareLogMessage(template, logMessage)) - file.close() - print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) - -check() - -config = shelve.open(configFile, writeback=True) - -if firstTime: - start(config) - -commits = config.get("commits", []) - -while len(commits) > 0: - firstTime = False - commit = commits[0] - commits = commits[1:] - config["commits"] = commits - apply(commit) - if not interactive: - break - -config.close() - -if len(commits) == 0: - if firstTime: - print "No changes found to apply between %s and current HEAD" % origin - else: - print "All changes applied!" - print "Deleting temporary p4-sync branch and going back to %s" % master - system("git checkout %s" % master) - system("git branch -D p4-sync") - print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." - system("p4 edit ... >/dev/null") - system("p4 revert ... >/dev/null") - os.remove(configFile) - -- cgit v1.2.3 From 0b69b469257424bcf015b7d3d03303c015c133f8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 20 Mar 2007 22:41:00 +0100 Subject: Start of the git-p4 documentation. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 74 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 contrib/fast-import/git-p4.txt (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt new file mode 100644 index 0000000000..b87efc6af9 --- /dev/null +++ b/contrib/fast-import/git-p4.txt @@ -0,0 +1,74 @@ +git-p4.py - Perforce <-> Git converter using git-fast-import + +Usage +===== + +git-p4 supports two main modes: Importing from Perforce to a Git repository is +done using "git-p4.py sync". Submitting changes from Git back to Perforce is +done using "git-p4.py submit". + +Importing +========= + +The procedure is simple: + + mkdir repo-git + cd repo-git + git init + git-p4.py sync //path/in/your/perforce/depot + +This will import the current head revision of the specified depot path into the +master branch of your git repository. You can use the --branch=mybranch option +to let git-p4 import from Perforce into a git branch of your choice. + +If you want to import the entire history of a given depot path just use + + git-p4.py sync //path/in/depot@all + + +Support for Perforce integrations is still work in progress. Don't bother +trying it unless you want to hack on it :) + + +Incremental Imports +=================== + +After an initial import you can easily synchronize your git repository with +newer changes from the Perforce depot by just calling + + git-p4.p4 sync + +in your git repository. git-p4 stores the depot path of the original import in +the .git/config file and remembers the last imported p4 revision as a git tag +called p4/ . + +Submitting +========== + +git-p4 has EXPERIMENTAL support for submitting changes from a git repository +back to a Perforce depot. This requires a Perforce checkout separate to your +git repository. This is the basic procedure: + + cd path/to/your/perforce/checkout + git-p4.py submit --git-dir=/path/to/your/git/repository + +This will create a temporary git branch, use git-rev-list to find out which git +commits are in your current branch but not in the "origin" branch. You can +override the name of the "origin" branch by using the --origin=mybranch option. +The "origin" branch has to be the branch populated with git-p4's sync +operation. + +After some preparations (which might take a while) git-p4 enters a loop where +it will first show a Perforce submit template and a diff of the change to +apply. After quitting the pager with 'q' git-p4 asks for confirmation for +issuing the "p4 submit" command and also gives you the option of editing the +submit template using "e". + +If a submit fails you may have to "p4 resolve" and submit manually. You can +continue importing the remaining changes with + + git-p4.py submit --git-dir=/path/to/your/git/repository --continue + +After submitting you should sync your origin branch from Perforce using +git-p4's sync command. + -- cgit v1.2.3 From b4aa8d12b48af84242a4a484673bc41326566ac9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Mar 2007 08:27:33 +0100 Subject: Documentation enhancements. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index b87efc6af9..4b4fcde72b 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -25,6 +25,8 @@ If you want to import the entire history of a given depot path just use git-p4.py sync //path/in/depot@all +To achieve optimal compression you may want to run 'git repack -a -d -f' after +a big import. This may take a while. Support for Perforce integrations is still work in progress. Don't bother trying it unless you want to hack on it :) @@ -42,6 +44,10 @@ in your git repository. git-p4 stores the depot path of the original import in the .git/config file and remembers the last imported p4 revision as a git tag called p4/ . +It is recommended to run 'git repack -a -d -f' from time to time when using +incremental imports to optimally combine the individual git packs that each +incremental import creates through the use of git-fast-import. + Submitting ========== -- cgit v1.2.3 From 04219c04b7554d13b049d4aba534ce02a12abeb4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Mar 2007 10:11:20 +0100 Subject: Added experimental but super-fast --apply-as-patch option to git-p4 submit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 54ddf73dac..2009dce23b 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -115,7 +115,8 @@ class P4Sync(Command): optparse.make_option("--master", dest="master"), optparse.make_option("--log-substitutions", dest="substFile"), optparse.make_option("--noninteractive", action="store_false"), - optparse.make_option("--dry-run", action="store_true") + optparse.make_option("--dry-run", action="store_true"), + optparse.make_option("--apply-as-patch", action="store_true", dest="applyAsPatch") ] self.description = "Submit changes from git to the perforce depot." self.firstTime = True @@ -126,6 +127,7 @@ class P4Sync(Command): self.firstTime = True self.origin = "origin" self.master = "" + self.applyAsPatch = False self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" @@ -146,8 +148,9 @@ class P4Sync(Command): self.config["commits"] = commits - print "Creating temporary p4-sync branch from %s ..." % self.origin - system("git checkout -f -b p4-sync %s" % self.origin) + if not self.applyAsPatch: + print "Creating temporary p4-sync branch from %s ..." % self.origin + system("git checkout -f -b p4-sync %s" % self.origin) def prepareLogMessage(self, template, message): result = "" @@ -193,8 +196,11 @@ class P4Sync(Command): else: die("unknown modifier %s for %s" % (modifier, path)) - system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") - system("git cherry-pick --no-commit \"%s\"" % id) + if self.applyAsPatch: + system("git-diff-tree -p \"%s^\" \"%s\" | patch -p1" % (id, id)) + else: + system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") + system("git cherry-pick --no-commit \"%s\"" % id) for f in filesToAdd: system("p4 add %s" % f) @@ -305,12 +311,13 @@ class P4Sync(Command): print "No changes found to apply between %s and current HEAD" % self.origin else: print "All changes applied!" - print "Deleting temporary p4-sync branch and going back to %s" % self.master - system("git checkout %s" % self.master) - system("git branch -D p4-sync") - print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." - system("p4 edit ... >/dev/null") - system("p4 revert ... >/dev/null") + if not self.applyAsPatch: + print "Deleting temporary p4-sync branch and going back to %s" % self.master + system("git checkout %s" % self.master) + system("git branch -D p4-sync") + print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." + system("p4 edit ... >/dev/null") + system("p4 revert ... >/dev/null") os.remove(self.configFile) return True -- cgit v1.2.3 From 5d0b6042d4a33dbd78840a8e0d8957441e2cfa72 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Mar 2007 10:57:54 +0100 Subject: Fix support for deletions in git-p4 submit when using --apply-as-patch by filtering out deletions in the diff-tree output. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 2009dce23b..336c3eab52 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -197,7 +197,7 @@ class P4Sync(Command): die("unknown modifier %s for %s" % (modifier, path)) if self.applyAsPatch: - system("git-diff-tree -p \"%s^\" \"%s\" | patch -p1" % (id, id)) + system("git-diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) else: system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") system("git cherry-pick --no-commit \"%s\"" % id) -- cgit v1.2.3 From 1932a6ac7c3725aec20574942e5eed9b1e1c5dee Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Mar 2007 11:01:18 +0100 Subject: Made --apply-as-patch the default for git-p4 submit as it's significantly faster. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index 336c3eab52..a07534058c 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -127,7 +127,7 @@ class P4Sync(Command): self.firstTime = True self.origin = "origin" self.master = "" - self.applyAsPatch = False + self.applyAsPatch = True self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" -- cgit v1.2.3 From 20618650058741117d909e086c2e78c003e5cfcd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Mar 2007 13:05:30 +0100 Subject: Make it possible to invoke git-p4 from within subdirectories of a git working tree. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index a07534058c..f940e93bad 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -925,6 +925,10 @@ parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), gitdir = cmd.gitdir if len(gitdir) == 0: gitdir = ".git" + if not isValidGitDir(gitdir): + cdup = os.popen("git-rev-parse --show-cdup").read()[:-1] + if isValidGitDir(cdup + "/" + gitdir): + os.chdir(cdup) if not isValidGitDir(gitdir): if isValidGitDir(gitdir + "/.git"): -- cgit v1.2.3 From 53150250b13e7621c8ade1d59d19c946b745dd39 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 21 Mar 2007 21:04:12 +0100 Subject: Don't show the submit template and the diff first in less but show it in $editor right away Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py index f940e93bad..06858844e5 100755 --- a/contrib/fast-import/git-p4.py +++ b/contrib/fast-import/git-p4.py @@ -235,24 +235,26 @@ class P4Sync(Command): diff += "+" + line f.close() - pipe = os.popen("less", "w") - pipe.write(submitTemplate + diff) - pipe.close() + separatorLine = "######## everything below this line is just the diff #######\n" response = "e" + firstIteration = True while response == "e": - response = raw_input("Do you want to submit this change (y/e/n)? ") + if not firstIteration: + response = raw_input("Do you want to submit this change (y/e/n)? ") + firstIteration = False if response == "e": [handle, fileName] = tempfile.mkstemp() tmpFile = os.fdopen(handle, "w+") - tmpFile.write(submitTemplate) + tmpFile.write(submitTemplate + separatorLine + diff) tmpFile.close() editor = os.environ.get("EDITOR", "vi") system(editor + " " + fileName) tmpFile = open(fileName, "r") - submitTemplate = tmpFile.read() + message = tmpFile.read() tmpFile.close() os.remove(fileName) + submitTemplate = message[:message.index(separatorLine)] if response == "y" or response == "yes": if self.dryRun: -- cgit v1.2.3 From e7f0d0d9b9dc99a3f748ed686625cc9f87d9a267 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 09:13:01 +0100 Subject: Removed the .py extension from git-p4 as it's annoying to type every time. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 945 +++++++++++++++++++++++++++++++++++++++++ contrib/fast-import/git-p4.py | 945 ----------------------------------------- contrib/fast-import/git-p4.txt | 14 +- 3 files changed, 952 insertions(+), 952 deletions(-) create mode 100755 contrib/fast-import/git-p4 delete mode 100755 contrib/fast-import/git-p4.py (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 new file mode 100755 index 0000000000..06858844e5 --- /dev/null +++ b/contrib/fast-import/git-p4 @@ -0,0 +1,945 @@ +#!/usr/bin/env python +# +# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git. +# +# Author: Simon Hausmann +# Copyright: 2007 Simon Hausmann +# 2007 Trolltech ASA +# License: MIT +# + +import optparse, sys, os, marshal, popen2, shelve +import tempfile, getopt, sha, os.path, time +from sets import Set; + +gitdir = os.environ.get("GIT_DIR", "") + +def p4CmdList(cmd): + cmd = "p4 -G %s" % cmd + pipe = os.popen(cmd, "rb") + + result = [] + try: + while True: + entry = marshal.load(pipe) + result.append(entry) + except EOFError: + pass + pipe.close() + + return result + +def p4Cmd(cmd): + list = p4CmdList(cmd) + result = {} + for entry in list: + result.update(entry) + return result; + +def die(msg): + sys.stderr.write(msg + "\n") + sys.exit(1) + +def currentGitBranch(): + return os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] + +def isValidGitDir(path): + if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): + return True; + return False + +def system(cmd): + if os.system(cmd) != 0: + die("command failed: %s" % cmd) + +class Command: + def __init__(self): + self.usage = "usage: %prog [options]" + +class P4Debug(Command): + def __init__(self): + self.options = [ + ] + self.description = "A tool to debug the output of p4 -G." + + def run(self, args): + for output in p4CmdList(" ".join(args)): + print output + return True + +class P4CleanTags(Command): + def __init__(self): + Command.__init__(self) + self.options = [ +# optparse.make_option("--branch", dest="branch", default="refs/heads/master") + ] + self.description = "A tool to remove stale unused tags from incremental perforce imports." + def run(self, args): + branch = currentGitBranch() + print "Cleaning out stale p4 import tags..." + sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) + output = sout.read() + try: + tagIdx = output.index(" tags/p4/") + except: + print "Cannot find any p4/* tag. Nothing to do." + sys.exit(0) + + try: + caretIdx = output.index("^") + except: + caretIdx = len(output) - 1 + rev = int(output[tagIdx + 9 : caretIdx]) + + allTags = os.popen("git tag -l p4/").readlines() + for i in range(len(allTags)): + allTags[i] = int(allTags[i][3:-1]) + + allTags.sort() + + allTags.remove(rev) + + for rev in allTags: + print os.popen("git tag -d p4/%s" % rev).read() + + print "%s tags removed." % len(allTags) + return True + +class P4Sync(Command): + def __init__(self): + Command.__init__(self) + self.options = [ + optparse.make_option("--continue", action="store_false", dest="firstTime"), + optparse.make_option("--origin", dest="origin"), + optparse.make_option("--reset", action="store_true", dest="reset"), + optparse.make_option("--master", dest="master"), + optparse.make_option("--log-substitutions", dest="substFile"), + optparse.make_option("--noninteractive", action="store_false"), + optparse.make_option("--dry-run", action="store_true"), + optparse.make_option("--apply-as-patch", action="store_true", dest="applyAsPatch") + ] + self.description = "Submit changes from git to the perforce depot." + self.firstTime = True + self.reset = False + self.interactive = True + self.dryRun = False + self.substFile = "" + self.firstTime = True + self.origin = "origin" + self.master = "" + self.applyAsPatch = True + + self.logSubstitutions = {} + self.logSubstitutions[""] = "%log%" + self.logSubstitutions["\tDetails:"] = "\tDetails: %log%" + + def check(self): + if len(p4CmdList("opened ...")) > 0: + die("You have files opened with perforce! Close them before starting the sync.") + + def start(self): + if len(self.config) > 0 and not self.reset: + die("Cannot start sync. Previous sync config found at %s" % self.configFile) + + commits = [] + for line in os.popen("git-rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): + commits.append(line[:-1]) + commits.reverse() + + self.config["commits"] = commits + + if not self.applyAsPatch: + print "Creating temporary p4-sync branch from %s ..." % self.origin + system("git checkout -f -b p4-sync %s" % self.origin) + + def prepareLogMessage(self, template, message): + result = "" + + for line in template.split("\n"): + if line.startswith("#"): + result += line + "\n" + continue + + substituted = False + for key in self.logSubstitutions.keys(): + if line.find(key) != -1: + value = self.logSubstitutions[key] + value = value.replace("%log%", message) + if value != "@remove@": + result += line.replace(key, value) + "\n" + substituted = True + break + + if not substituted: + result += line + "\n" + + return result + + def apply(self, id): + print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) + diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() + filesToAdd = set() + filesToDelete = set() + for line in diff: + modifier = line[0] + path = line[1:].strip() + if modifier == "M": + system("p4 edit %s" % path) + elif modifier == "A": + filesToAdd.add(path) + if path in filesToDelete: + filesToDelete.remove(path) + elif modifier == "D": + filesToDelete.add(path) + if path in filesToAdd: + filesToAdd.remove(path) + else: + die("unknown modifier %s for %s" % (modifier, path)) + + if self.applyAsPatch: + system("git-diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) + else: + system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") + system("git cherry-pick --no-commit \"%s\"" % id) + + for f in filesToAdd: + system("p4 add %s" % f) + for f in filesToDelete: + system("p4 revert %s" % f) + system("p4 delete %s" % f) + + logMessage = "" + foundTitle = False + for log in os.popen("git-cat-file commit %s" % id).readlines(): + if not foundTitle: + if len(log) == 1: + foundTitle = 1 + continue + + if len(logMessage) > 0: + logMessage += "\t" + logMessage += log + + template = os.popen("p4 change -o").read() + + if self.interactive: + submitTemplate = self.prepareLogMessage(template, logMessage) + diff = os.popen("p4 diff -du ...").read() + + for newFile in filesToAdd: + diff += "==== new file ====\n" + diff += "--- /dev/null\n" + diff += "+++ %s\n" % newFile + f = open(newFile, "r") + for line in f.readlines(): + diff += "+" + line + f.close() + + separatorLine = "######## everything below this line is just the diff #######\n" + + response = "e" + firstIteration = True + while response == "e": + if not firstIteration: + response = raw_input("Do you want to submit this change (y/e/n)? ") + firstIteration = False + if response == "e": + [handle, fileName] = tempfile.mkstemp() + tmpFile = os.fdopen(handle, "w+") + tmpFile.write(submitTemplate + separatorLine + diff) + tmpFile.close() + editor = os.environ.get("EDITOR", "vi") + system(editor + " " + fileName) + tmpFile = open(fileName, "r") + message = tmpFile.read() + tmpFile.close() + os.remove(fileName) + submitTemplate = message[:message.index(separatorLine)] + + if response == "y" or response == "yes": + if self.dryRun: + print submitTemplate + raw_input("Press return to continue...") + else: + pipe = os.popen("p4 submit -i", "w") + pipe.write(submitTemplate) + pipe.close() + else: + print "Not submitting!" + self.interactive = False + else: + fileName = "submit.txt" + file = open(fileName, "w+") + file.write(self.prepareLogMessage(template, logMessage)) + file.close() + print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) + + def run(self, args): + if self.reset: + self.firstTime = True + + if len(self.substFile) > 0: + for line in open(self.substFile, "r").readlines(): + tokens = line[:-1].split("=") + self.logSubstitutions[tokens[0]] = tokens[1] + + if len(self.master) == 0: + self.master = currentGitBranch() + if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)): + die("Detecting current git branch failed!") + + self.check() + self.configFile = gitdir + "/p4-git-sync.cfg" + self.config = shelve.open(self.configFile, writeback=True) + + if self.firstTime: + self.start() + + commits = self.config.get("commits", []) + + while len(commits) > 0: + self.firstTime = False + commit = commits[0] + commits = commits[1:] + self.config["commits"] = commits + self.apply(commit) + if not self.interactive: + break + + self.config.close() + + if len(commits) == 0: + if self.firstTime: + print "No changes found to apply between %s and current HEAD" % self.origin + else: + print "All changes applied!" + if not self.applyAsPatch: + print "Deleting temporary p4-sync branch and going back to %s" % self.master + system("git checkout %s" % self.master) + system("git branch -D p4-sync") + print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." + system("p4 edit ... >/dev/null") + system("p4 revert ... >/dev/null") + os.remove(self.configFile) + + return True + +class GitSync(Command): + def __init__(self): + Command.__init__(self) + self.options = [ + optparse.make_option("--branch", dest="branch"), + optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"), + optparse.make_option("--changesfile", dest="changesFile"), + optparse.make_option("--silent", dest="silent", action="store_true"), + optparse.make_option("--known-branches", dest="knownBranches"), + optparse.make_option("--cache", dest="doCache", action="store_true"), + optparse.make_option("--command-cache", dest="commandCache", action="store_true") + ] + self.description = """Imports from Perforce into a git repository.\n + example: + //depot/my/project/ -- to import the current head + //depot/my/project/@all -- to import everything + //depot/my/project/@1,6 -- to import only from revision 1 to 6 + + (a ... is not needed in the path p4 specification, it's added implicitly)""" + + self.usage += " //depot/path[@revRange]" + + self.dataCache = False + self.commandCache = False + self.silent = False + self.knownBranches = Set() + self.createdBranches = Set() + self.committedChanges = Set() + self.branch = "master" + self.detectBranches = False + self.changesFile = "" + + def p4File(self, depotPath): + return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + + def extractFilesFromCommit(self, commit): + files = [] + fnum = 0 + while commit.has_key("depotFile%s" % fnum): + path = commit["depotFile%s" % fnum] + if not path.startswith(self.globalPrefix): + # if not self.silent: + # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.globalPrefix, change) + fnum = fnum + 1 + continue + + file = {} + file["path"] = path + file["rev"] = commit["rev%s" % fnum] + file["action"] = commit["action%s" % fnum] + file["type"] = commit["type%s" % fnum] + files.append(file) + fnum = fnum + 1 + return files + + def isSubPathOf(self, first, second): + if not first.startswith(second): + return False + if first == second: + return True + return first[len(second)] == "/" + + def branchesForCommit(self, files): + branches = Set() + + for file in files: + relativePath = file["path"][len(self.globalPrefix):] + # strip off the filename + relativePath = relativePath[0:relativePath.rfind("/")] + + # if len(branches) == 0: + # branches.add(relativePath) + # knownBranches.add(relativePath) + # continue + + ###### this needs more testing :) + knownBranch = False + for branch in branches: + if relativePath == branch: + knownBranch = True + break + # if relativePath.startswith(branch): + if self.isSubPathOf(relativePath, branch): + knownBranch = True + break + # if branch.startswith(relativePath): + if self.isSubPathOf(branch, relativePath): + branches.remove(branch) + break + + if knownBranch: + continue + + for branch in knownBranches: + #if relativePath.startswith(branch): + if self.isSubPathOf(relativePath, branch): + if len(branches) == 0: + relativePath = branch + else: + knownBranch = True + break + + if knownBranch: + continue + + branches.add(relativePath) + self.knownBranches.add(relativePath) + + return branches + + def findBranchParent(self, branchPrefix, files): + for file in files: + path = file["path"] + if not path.startswith(branchPrefix): + continue + action = file["action"] + if action != "integrate" and action != "branch": + continue + rev = file["rev"] + depotPath = path + "#" + rev + + log = p4CmdList("filelog \"%s\"" % depotPath) + if len(log) != 1: + print "eek! I got confused by the filelog of %s" % depotPath + sys.exit(1); + + log = log[0] + if log["action0"] != action: + print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) + sys.exit(1); + + branchAction = log["how0,0"] + # if branchAction == "branch into" or branchAction == "ignored": + # continue # ignore for branching + + if not branchAction.endswith(" from"): + continue # ignore for branching + # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) + # sys.exit(1); + + source = log["file0,0"] + if source.startswith(branchPrefix): + continue + + lastSourceRev = log["erev0,0"] + + sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) + if len(sourceLog) != 1: + print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) + sys.exit(1); + sourceLog = sourceLog[0] + + relPath = source[len(self.globalPrefix):] + # strip off the filename + relPath = relPath[0:relPath.rfind("/")] + + for branch in self.knownBranches: + if self.isSubPathOf(relPath, branch): + # print "determined parent branch branch %s due to change in file %s" % (branch, source) + return branch + # else: + # print "%s is not a subpath of branch %s" % (relPath, branch) + + return "" + + def commit(self, details, files, branch, branchPrefix, parent = "", merged = ""): + epoch = details["time"] + author = details["user"] + + self.gitStream.write("commit %s\n" % branch) + # gitStream.write("mark :%s\n" % details["change"]) + self.committedChanges.add(int(details["change"])) + committer = "" + if author in self.users: + committer = "%s %s %s" % (self.users[author], epoch, self.tz) + else: + committer = "%s %s %s" % (author, epoch, self.tz) + + self.gitStream.write("committer %s\n" % committer) + + self.gitStream.write("data < 0: + self.gitStream.write("from %s\n" % parent) + + if len(merged) > 0: + self.gitStream.write("merge %s\n" % merged) + + for file in files: + path = file["path"] + if not path.startswith(branchPrefix): + # if not silent: + # print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) + continue + rev = file["rev"] + depotPath = path + "#" + rev + relPath = path[len(branchPrefix):] + action = file["action"] + + if file["type"] == "apple": + print "\nfile %s is a strange apple file that forks. Ignoring!" % path + continue + + if action == "delete": + self.gitStream.write("D %s\n" % relPath) + else: + mode = 644 + if file["type"].startswith("x"): + mode = 755 + + data = self.p4File(depotPath) + + self.gitStream.write("M %s inline %s\n" % (mode, relPath)) + self.gitStream.write("data %s\n" % len(data)) + self.gitStream.write(data) + self.gitStream.write("\n") + + self.gitStream.write("\n") + + self.lastChange = int(details["change"]) + + def extractFilesInCommitToBranch(self, files, branchPrefix): + newFiles = [] + + for file in files: + path = file["path"] + if path.startswith(branchPrefix): + newFiles.append(file) + + return newFiles + + def findBranchSourceHeuristic(self, files, branch, branchPrefix): + for file in files: + action = file["action"] + if action != "integrate" and action != "branch": + continue + path = file["path"] + rev = file["rev"] + depotPath = path + "#" + rev + + log = p4CmdList("filelog \"%s\"" % depotPath) + if len(log) != 1: + print "eek! I got confused by the filelog of %s" % depotPath + sys.exit(1); + + log = log[0] + if log["action0"] != action: + print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) + sys.exit(1); + + branchAction = log["how0,0"] + + if not branchAction.endswith(" from"): + continue # ignore for branching + # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) + # sys.exit(1); + + source = log["file0,0"] + if source.startswith(branchPrefix): + continue + + lastSourceRev = log["erev0,0"] + + sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) + if len(sourceLog) != 1: + print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) + sys.exit(1); + sourceLog = sourceLog[0] + + relPath = source[len(self.globalPrefix):] + # strip off the filename + relPath = relPath[0:relPath.rfind("/")] + + for candidate in self.knownBranches: + if self.isSubPathOf(relPath, candidate) and candidate != branch: + return candidate + + return "" + + def changeIsBranchMerge(self, sourceBranch, destinationBranch, change): + sourceFiles = {} + for file in p4CmdList("files %s...@%s" % (self.globalPrefix + sourceBranch + "/", change)): + if file["action"] == "delete": + continue + sourceFiles[file["depotFile"]] = file + + destinationFiles = {} + for file in p4CmdList("files %s...@%s" % (self.globalPrefix + destinationBranch + "/", change)): + destinationFiles[file["depotFile"]] = file + + for fileName in sourceFiles.keys(): + integrations = [] + deleted = False + integrationCount = 0 + for integration in p4CmdList("integrated \"%s\"" % fileName): + toFile = integration["fromFile"] # yes, it's true, it's fromFile + if not toFile in destinationFiles: + continue + destFile = destinationFiles[toFile] + if destFile["action"] == "delete": + # print "file %s has been deleted in %s" % (fileName, toFile) + deleted = True + break + integrationCount += 1 + if integration["how"] == "branch from": + continue + + if int(integration["change"]) == change: + integrations.append(integration) + continue + if int(integration["change"]) > change: + continue + + destRev = int(destFile["rev"]) + + startRev = integration["startFromRev"][1:] + if startRev == "none": + startRev = 0 + else: + startRev = int(startRev) + + endRev = integration["endFromRev"][1:] + if endRev == "none": + endRev = 0 + else: + endRev = int(endRev) + + initialBranch = (destRev == 1 and integration["how"] != "branch into") + inRange = (destRev >= startRev and destRev <= endRev) + newer = (destRev > startRev and destRev > endRev) + + if initialBranch or inRange or newer: + integrations.append(integration) + + if deleted: + continue + + if len(integrations) == 0 and integrationCount > 1: + print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) + return False + + return True + + def getUserMap(self): + self.users = {} + + for output in p4CmdList("users"): + if not output.has_key("User"): + continue + self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" + + def run(self, args): + self.branch = "refs/heads/" + self.branch + self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() + if len(self.globalPrefix) != 0: + self.globalPrefix = self.globalPrefix[:-1] + + if len(args) == 0 and len(self.globalPrefix) != 0: + if not self.silent: + print "[using previously specified depot path %s]" % self.globalPrefix + elif len(args) != 1: + return False + else: + if len(self.globalPrefix) != 0 and self.globalPrefix != args[0]: + print "previous import used depot path %s and now %s was specified. this doesn't work!" % (self.globalPrefix, args[0]) + sys.exit(1) + self.globalPrefix = args[0] + + self.changeRange = "" + self.revision = "" + self.users = {} + self.initialParent = "" + self.lastChange = 0 + self.initialTag = "" + + if self.globalPrefix.find("@") != -1: + atIdx = self.globalPrefix.index("@") + self.changeRange = self.globalPrefix[atIdx:] + if self.changeRange == "@all": + self.changeRange = "" + elif self.changeRange.find(",") == -1: + self.revision = self.changeRange + self.changeRange = "" + self.globalPrefix = self.globalPrefix[0:atIdx] + elif self.globalPrefix.find("#") != -1: + hashIdx = self.globalPrefix.index("#") + self.revision = self.globalPrefix[hashIdx:] + self.globalPrefix = self.globalPrefix[0:hashIdx] + elif len(self.previousDepotPath) == 0: + self.revision = "#head" + + if self.globalPrefix.endswith("..."): + self.globalPrefix = self.globalPrefix[:-3] + + if not self.globalPrefix.endswith("/"): + self.globalPrefix += "/" + + self.getUserMap() + + if len(self.changeRange) == 0: + try: + sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % self.branch) + output = sout.read() + if output.endswith("\n"): + output = output[:-1] + tagIdx = output.index(" tags/p4/") + caretIdx = output.find("^") + endPos = len(output) + if caretIdx != -1: + endPos = caretIdx + self.rev = int(output[tagIdx + 9 : endPos]) + 1 + self.changeRange = "@%s,#head" % self.rev + self.initialParent = os.popen("git-rev-parse %s" % self.branch).read()[:-1] + self.initialTag = "p4/%s" % (int(self.rev) - 1) + except: + pass + + self.tz = - time.timezone / 36 + tzsign = ("%s" % self.tz)[0] + if tzsign != '+' and tzsign != '-': + self.tz = "+" + ("%s" % self.tz) + + self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git-fast-import") + + if len(self.revision) > 0: + print "Doing initial import of %s from revision %s" % (self.globalPrefix, self.revision) + + details = { "user" : "git perforce import user", "time" : int(time.time()) } + details["desc"] = "Initial import of %s from the state at revision %s" % (self.globalPrefix, self.revision) + details["change"] = self.revision + newestRevision = 0 + + fileCnt = 0 + for info in p4CmdList("files %s...%s" % (self.globalPrefix, self.revision)): + change = int(info["change"]) + if change > newestRevision: + newestRevision = change + + if info["action"] == "delete": + fileCnt = fileCnt + 1 + continue + + for prop in [ "depotFile", "rev", "action", "type" ]: + details["%s%s" % (prop, fileCnt)] = info[prop] + + fileCnt = fileCnt + 1 + + details["change"] = newestRevision + + try: + self.commit(details, self.extractFilesFromCommit(details), self.branch, self.globalPrefix) + except IOError: + print self.gitError.read() + + else: + changes = [] + + if len(self.changesFile) > 0: + output = open(self.changesFile).readlines() + changeSet = Set() + for line in output: + changeSet.add(int(line)) + + for change in changeSet: + changes.append(change) + + changes.sort() + else: + output = os.popen("p4 changes %s...%s" % (self.globalPrefix, self.changeRange)).readlines() + + for line in output: + changeNum = line.split(" ")[1] + changes.append(changeNum) + + changes.reverse() + + if len(changes) == 0: + if not self.silent: + print "no changes to import!" + sys.exit(1) + + cnt = 1 + for change in changes: + description = p4Cmd("describe %s" % change) + + if not self.silent: + sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() + cnt = cnt + 1 + + try: + files = self.extractFilesFromCommit(description) + if self.detectBranches: + for branch in self.branchesForCommit(files): + self.knownBranches.add(branch) + branchPrefix = self.globalPrefix + branch + "/" + + filesForCommit = self.extractFilesInCommitToBranch(files, branchPrefix) + + merged = "" + parent = "" + ########### remove cnt!!! + if branch not in self.createdBranches and cnt > 2: + self.createdBranches.add(branch) + parent = self.findBranchParent(branchPrefix, files) + if parent == branch: + parent = "" + # elif len(parent) > 0: + # print "%s branched off of %s" % (branch, parent) + + if len(parent) == 0: + merged = self.findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) + if len(merged) > 0: + print "change %s could be a merge from %s into %s" % (description["change"], merged, branch) + if not self.changeIsBranchMerge(merged, branch, int(description["change"])): + merged = "" + + branch = "refs/heads/" + branch + if len(parent) > 0: + parent = "refs/heads/" + parent + if len(merged) > 0: + merged = "refs/heads/" + merged + self.commit(description, files, branch, branchPrefix, parent, merged) + else: + self.commit(description, files, self.branch, self.globalPrefix, self.initialParent) + self.initialParent = "" + except IOError: + print self.gitError.read() + sys.exit(1) + + if not self.silent: + print "" + + self.gitStream.write("reset refs/tags/p4/%s\n" % self.lastChange) + self.gitStream.write("from %s\n\n" % self.branch); + + + self.gitStream.close() + self.gitOutput.close() + self.gitError.close() + + os.popen("git-repo-config p4.depotpath %s" % self.globalPrefix).read() + if len(self.initialTag) > 0: + os.popen("git tag -d %s" % self.initialTag).read() + + return True + +class HelpFormatter(optparse.IndentedHelpFormatter): + def __init__(self): + optparse.IndentedHelpFormatter.__init__(self) + + def format_description(self, description): + if description: + return description + "\n" + else: + return "" + +def printUsage(commands): + print "usage: %s [options]" % sys.argv[0] + print "" + print "valid commands: %s" % ", ".join(commands) + print "" + print "Try %s --help for command specific help." % sys.argv[0] + print "" + +commands = { + "debug" : P4Debug(), + "clean-tags" : P4CleanTags(), + "submit" : P4Sync(), + "sync" : GitSync() +} + +if len(sys.argv[1:]) == 0: + printUsage(commands.keys()) + sys.exit(2) + +cmd = "" +cmdName = sys.argv[1] +try: + cmd = commands[cmdName] +except KeyError: + print "unknown command %s" % cmdName + print "" + printUsage(commands.keys()) + sys.exit(2) + +options = cmd.options +cmd.gitdir = gitdir +options.append(optparse.make_option("--git-dir", dest="gitdir")) + +parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), + options, + description = cmd.description, + formatter = HelpFormatter()) + +(cmd, args) = parser.parse_args(sys.argv[2:], cmd); + +gitdir = cmd.gitdir +if len(gitdir) == 0: + gitdir = ".git" + if not isValidGitDir(gitdir): + cdup = os.popen("git-rev-parse --show-cdup").read()[:-1] + if isValidGitDir(cdup + "/" + gitdir): + os.chdir(cdup) + +if not isValidGitDir(gitdir): + if isValidGitDir(gitdir + "/.git"): + gitdir += "/.git" + else: + die("fatal: cannot locate git repository at %s" % gitdir) + +os.environ["GIT_DIR"] = gitdir + +if not cmd.run(args): + parser.print_help() + diff --git a/contrib/fast-import/git-p4.py b/contrib/fast-import/git-p4.py deleted file mode 100755 index 06858844e5..0000000000 --- a/contrib/fast-import/git-p4.py +++ /dev/null @@ -1,945 +0,0 @@ -#!/usr/bin/env python -# -# git-p4.py -- A tool for bidirectional operation between a Perforce depot and git. -# -# Author: Simon Hausmann -# Copyright: 2007 Simon Hausmann -# 2007 Trolltech ASA -# License: MIT -# - -import optparse, sys, os, marshal, popen2, shelve -import tempfile, getopt, sha, os.path, time -from sets import Set; - -gitdir = os.environ.get("GIT_DIR", "") - -def p4CmdList(cmd): - cmd = "p4 -G %s" % cmd - pipe = os.popen(cmd, "rb") - - result = [] - try: - while True: - entry = marshal.load(pipe) - result.append(entry) - except EOFError: - pass - pipe.close() - - return result - -def p4Cmd(cmd): - list = p4CmdList(cmd) - result = {} - for entry in list: - result.update(entry) - return result; - -def die(msg): - sys.stderr.write(msg + "\n") - sys.exit(1) - -def currentGitBranch(): - return os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] - -def isValidGitDir(path): - if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): - return True; - return False - -def system(cmd): - if os.system(cmd) != 0: - die("command failed: %s" % cmd) - -class Command: - def __init__(self): - self.usage = "usage: %prog [options]" - -class P4Debug(Command): - def __init__(self): - self.options = [ - ] - self.description = "A tool to debug the output of p4 -G." - - def run(self, args): - for output in p4CmdList(" ".join(args)): - print output - return True - -class P4CleanTags(Command): - def __init__(self): - Command.__init__(self) - self.options = [ -# optparse.make_option("--branch", dest="branch", default="refs/heads/master") - ] - self.description = "A tool to remove stale unused tags from incremental perforce imports." - def run(self, args): - branch = currentGitBranch() - print "Cleaning out stale p4 import tags..." - sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) - output = sout.read() - try: - tagIdx = output.index(" tags/p4/") - except: - print "Cannot find any p4/* tag. Nothing to do." - sys.exit(0) - - try: - caretIdx = output.index("^") - except: - caretIdx = len(output) - 1 - rev = int(output[tagIdx + 9 : caretIdx]) - - allTags = os.popen("git tag -l p4/").readlines() - for i in range(len(allTags)): - allTags[i] = int(allTags[i][3:-1]) - - allTags.sort() - - allTags.remove(rev) - - for rev in allTags: - print os.popen("git tag -d p4/%s" % rev).read() - - print "%s tags removed." % len(allTags) - return True - -class P4Sync(Command): - def __init__(self): - Command.__init__(self) - self.options = [ - optparse.make_option("--continue", action="store_false", dest="firstTime"), - optparse.make_option("--origin", dest="origin"), - optparse.make_option("--reset", action="store_true", dest="reset"), - optparse.make_option("--master", dest="master"), - optparse.make_option("--log-substitutions", dest="substFile"), - optparse.make_option("--noninteractive", action="store_false"), - optparse.make_option("--dry-run", action="store_true"), - optparse.make_option("--apply-as-patch", action="store_true", dest="applyAsPatch") - ] - self.description = "Submit changes from git to the perforce depot." - self.firstTime = True - self.reset = False - self.interactive = True - self.dryRun = False - self.substFile = "" - self.firstTime = True - self.origin = "origin" - self.master = "" - self.applyAsPatch = True - - self.logSubstitutions = {} - self.logSubstitutions[""] = "%log%" - self.logSubstitutions["\tDetails:"] = "\tDetails: %log%" - - def check(self): - if len(p4CmdList("opened ...")) > 0: - die("You have files opened with perforce! Close them before starting the sync.") - - def start(self): - if len(self.config) > 0 and not self.reset: - die("Cannot start sync. Previous sync config found at %s" % self.configFile) - - commits = [] - for line in os.popen("git-rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): - commits.append(line[:-1]) - commits.reverse() - - self.config["commits"] = commits - - if not self.applyAsPatch: - print "Creating temporary p4-sync branch from %s ..." % self.origin - system("git checkout -f -b p4-sync %s" % self.origin) - - def prepareLogMessage(self, template, message): - result = "" - - for line in template.split("\n"): - if line.startswith("#"): - result += line + "\n" - continue - - substituted = False - for key in self.logSubstitutions.keys(): - if line.find(key) != -1: - value = self.logSubstitutions[key] - value = value.replace("%log%", message) - if value != "@remove@": - result += line.replace(key, value) + "\n" - substituted = True - break - - if not substituted: - result += line + "\n" - - return result - - def apply(self, id): - print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) - diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() - filesToAdd = set() - filesToDelete = set() - for line in diff: - modifier = line[0] - path = line[1:].strip() - if modifier == "M": - system("p4 edit %s" % path) - elif modifier == "A": - filesToAdd.add(path) - if path in filesToDelete: - filesToDelete.remove(path) - elif modifier == "D": - filesToDelete.add(path) - if path in filesToAdd: - filesToAdd.remove(path) - else: - die("unknown modifier %s for %s" % (modifier, path)) - - if self.applyAsPatch: - system("git-diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) - else: - system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") - system("git cherry-pick --no-commit \"%s\"" % id) - - for f in filesToAdd: - system("p4 add %s" % f) - for f in filesToDelete: - system("p4 revert %s" % f) - system("p4 delete %s" % f) - - logMessage = "" - foundTitle = False - for log in os.popen("git-cat-file commit %s" % id).readlines(): - if not foundTitle: - if len(log) == 1: - foundTitle = 1 - continue - - if len(logMessage) > 0: - logMessage += "\t" - logMessage += log - - template = os.popen("p4 change -o").read() - - if self.interactive: - submitTemplate = self.prepareLogMessage(template, logMessage) - diff = os.popen("p4 diff -du ...").read() - - for newFile in filesToAdd: - diff += "==== new file ====\n" - diff += "--- /dev/null\n" - diff += "+++ %s\n" % newFile - f = open(newFile, "r") - for line in f.readlines(): - diff += "+" + line - f.close() - - separatorLine = "######## everything below this line is just the diff #######\n" - - response = "e" - firstIteration = True - while response == "e": - if not firstIteration: - response = raw_input("Do you want to submit this change (y/e/n)? ") - firstIteration = False - if response == "e": - [handle, fileName] = tempfile.mkstemp() - tmpFile = os.fdopen(handle, "w+") - tmpFile.write(submitTemplate + separatorLine + diff) - tmpFile.close() - editor = os.environ.get("EDITOR", "vi") - system(editor + " " + fileName) - tmpFile = open(fileName, "r") - message = tmpFile.read() - tmpFile.close() - os.remove(fileName) - submitTemplate = message[:message.index(separatorLine)] - - if response == "y" or response == "yes": - if self.dryRun: - print submitTemplate - raw_input("Press return to continue...") - else: - pipe = os.popen("p4 submit -i", "w") - pipe.write(submitTemplate) - pipe.close() - else: - print "Not submitting!" - self.interactive = False - else: - fileName = "submit.txt" - file = open(fileName, "w+") - file.write(self.prepareLogMessage(template, logMessage)) - file.close() - print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) - - def run(self, args): - if self.reset: - self.firstTime = True - - if len(self.substFile) > 0: - for line in open(self.substFile, "r").readlines(): - tokens = line[:-1].split("=") - self.logSubstitutions[tokens[0]] = tokens[1] - - if len(self.master) == 0: - self.master = currentGitBranch() - if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)): - die("Detecting current git branch failed!") - - self.check() - self.configFile = gitdir + "/p4-git-sync.cfg" - self.config = shelve.open(self.configFile, writeback=True) - - if self.firstTime: - self.start() - - commits = self.config.get("commits", []) - - while len(commits) > 0: - self.firstTime = False - commit = commits[0] - commits = commits[1:] - self.config["commits"] = commits - self.apply(commit) - if not self.interactive: - break - - self.config.close() - - if len(commits) == 0: - if self.firstTime: - print "No changes found to apply between %s and current HEAD" % self.origin - else: - print "All changes applied!" - if not self.applyAsPatch: - print "Deleting temporary p4-sync branch and going back to %s" % self.master - system("git checkout %s" % self.master) - system("git branch -D p4-sync") - print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." - system("p4 edit ... >/dev/null") - system("p4 revert ... >/dev/null") - os.remove(self.configFile) - - return True - -class GitSync(Command): - def __init__(self): - Command.__init__(self) - self.options = [ - optparse.make_option("--branch", dest="branch"), - optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"), - optparse.make_option("--changesfile", dest="changesFile"), - optparse.make_option("--silent", dest="silent", action="store_true"), - optparse.make_option("--known-branches", dest="knownBranches"), - optparse.make_option("--cache", dest="doCache", action="store_true"), - optparse.make_option("--command-cache", dest="commandCache", action="store_true") - ] - self.description = """Imports from Perforce into a git repository.\n - example: - //depot/my/project/ -- to import the current head - //depot/my/project/@all -- to import everything - //depot/my/project/@1,6 -- to import only from revision 1 to 6 - - (a ... is not needed in the path p4 specification, it's added implicitly)""" - - self.usage += " //depot/path[@revRange]" - - self.dataCache = False - self.commandCache = False - self.silent = False - self.knownBranches = Set() - self.createdBranches = Set() - self.committedChanges = Set() - self.branch = "master" - self.detectBranches = False - self.changesFile = "" - - def p4File(self, depotPath): - return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() - - def extractFilesFromCommit(self, commit): - files = [] - fnum = 0 - while commit.has_key("depotFile%s" % fnum): - path = commit["depotFile%s" % fnum] - if not path.startswith(self.globalPrefix): - # if not self.silent: - # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.globalPrefix, change) - fnum = fnum + 1 - continue - - file = {} - file["path"] = path - file["rev"] = commit["rev%s" % fnum] - file["action"] = commit["action%s" % fnum] - file["type"] = commit["type%s" % fnum] - files.append(file) - fnum = fnum + 1 - return files - - def isSubPathOf(self, first, second): - if not first.startswith(second): - return False - if first == second: - return True - return first[len(second)] == "/" - - def branchesForCommit(self, files): - branches = Set() - - for file in files: - relativePath = file["path"][len(self.globalPrefix):] - # strip off the filename - relativePath = relativePath[0:relativePath.rfind("/")] - - # if len(branches) == 0: - # branches.add(relativePath) - # knownBranches.add(relativePath) - # continue - - ###### this needs more testing :) - knownBranch = False - for branch in branches: - if relativePath == branch: - knownBranch = True - break - # if relativePath.startswith(branch): - if self.isSubPathOf(relativePath, branch): - knownBranch = True - break - # if branch.startswith(relativePath): - if self.isSubPathOf(branch, relativePath): - branches.remove(branch) - break - - if knownBranch: - continue - - for branch in knownBranches: - #if relativePath.startswith(branch): - if self.isSubPathOf(relativePath, branch): - if len(branches) == 0: - relativePath = branch - else: - knownBranch = True - break - - if knownBranch: - continue - - branches.add(relativePath) - self.knownBranches.add(relativePath) - - return branches - - def findBranchParent(self, branchPrefix, files): - for file in files: - path = file["path"] - if not path.startswith(branchPrefix): - continue - action = file["action"] - if action != "integrate" and action != "branch": - continue - rev = file["rev"] - depotPath = path + "#" + rev - - log = p4CmdList("filelog \"%s\"" % depotPath) - if len(log) != 1: - print "eek! I got confused by the filelog of %s" % depotPath - sys.exit(1); - - log = log[0] - if log["action0"] != action: - print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) - sys.exit(1); - - branchAction = log["how0,0"] - # if branchAction == "branch into" or branchAction == "ignored": - # continue # ignore for branching - - if not branchAction.endswith(" from"): - continue # ignore for branching - # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) - # sys.exit(1); - - source = log["file0,0"] - if source.startswith(branchPrefix): - continue - - lastSourceRev = log["erev0,0"] - - sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) - if len(sourceLog) != 1: - print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) - sys.exit(1); - sourceLog = sourceLog[0] - - relPath = source[len(self.globalPrefix):] - # strip off the filename - relPath = relPath[0:relPath.rfind("/")] - - for branch in self.knownBranches: - if self.isSubPathOf(relPath, branch): - # print "determined parent branch branch %s due to change in file %s" % (branch, source) - return branch - # else: - # print "%s is not a subpath of branch %s" % (relPath, branch) - - return "" - - def commit(self, details, files, branch, branchPrefix, parent = "", merged = ""): - epoch = details["time"] - author = details["user"] - - self.gitStream.write("commit %s\n" % branch) - # gitStream.write("mark :%s\n" % details["change"]) - self.committedChanges.add(int(details["change"])) - committer = "" - if author in self.users: - committer = "%s %s %s" % (self.users[author], epoch, self.tz) - else: - committer = "%s %s %s" % (author, epoch, self.tz) - - self.gitStream.write("committer %s\n" % committer) - - self.gitStream.write("data < 0: - self.gitStream.write("from %s\n" % parent) - - if len(merged) > 0: - self.gitStream.write("merge %s\n" % merged) - - for file in files: - path = file["path"] - if not path.startswith(branchPrefix): - # if not silent: - # print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) - continue - rev = file["rev"] - depotPath = path + "#" + rev - relPath = path[len(branchPrefix):] - action = file["action"] - - if file["type"] == "apple": - print "\nfile %s is a strange apple file that forks. Ignoring!" % path - continue - - if action == "delete": - self.gitStream.write("D %s\n" % relPath) - else: - mode = 644 - if file["type"].startswith("x"): - mode = 755 - - data = self.p4File(depotPath) - - self.gitStream.write("M %s inline %s\n" % (mode, relPath)) - self.gitStream.write("data %s\n" % len(data)) - self.gitStream.write(data) - self.gitStream.write("\n") - - self.gitStream.write("\n") - - self.lastChange = int(details["change"]) - - def extractFilesInCommitToBranch(self, files, branchPrefix): - newFiles = [] - - for file in files: - path = file["path"] - if path.startswith(branchPrefix): - newFiles.append(file) - - return newFiles - - def findBranchSourceHeuristic(self, files, branch, branchPrefix): - for file in files: - action = file["action"] - if action != "integrate" and action != "branch": - continue - path = file["path"] - rev = file["rev"] - depotPath = path + "#" + rev - - log = p4CmdList("filelog \"%s\"" % depotPath) - if len(log) != 1: - print "eek! I got confused by the filelog of %s" % depotPath - sys.exit(1); - - log = log[0] - if log["action0"] != action: - print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) - sys.exit(1); - - branchAction = log["how0,0"] - - if not branchAction.endswith(" from"): - continue # ignore for branching - # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) - # sys.exit(1); - - source = log["file0,0"] - if source.startswith(branchPrefix): - continue - - lastSourceRev = log["erev0,0"] - - sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) - if len(sourceLog) != 1: - print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) - sys.exit(1); - sourceLog = sourceLog[0] - - relPath = source[len(self.globalPrefix):] - # strip off the filename - relPath = relPath[0:relPath.rfind("/")] - - for candidate in self.knownBranches: - if self.isSubPathOf(relPath, candidate) and candidate != branch: - return candidate - - return "" - - def changeIsBranchMerge(self, sourceBranch, destinationBranch, change): - sourceFiles = {} - for file in p4CmdList("files %s...@%s" % (self.globalPrefix + sourceBranch + "/", change)): - if file["action"] == "delete": - continue - sourceFiles[file["depotFile"]] = file - - destinationFiles = {} - for file in p4CmdList("files %s...@%s" % (self.globalPrefix + destinationBranch + "/", change)): - destinationFiles[file["depotFile"]] = file - - for fileName in sourceFiles.keys(): - integrations = [] - deleted = False - integrationCount = 0 - for integration in p4CmdList("integrated \"%s\"" % fileName): - toFile = integration["fromFile"] # yes, it's true, it's fromFile - if not toFile in destinationFiles: - continue - destFile = destinationFiles[toFile] - if destFile["action"] == "delete": - # print "file %s has been deleted in %s" % (fileName, toFile) - deleted = True - break - integrationCount += 1 - if integration["how"] == "branch from": - continue - - if int(integration["change"]) == change: - integrations.append(integration) - continue - if int(integration["change"]) > change: - continue - - destRev = int(destFile["rev"]) - - startRev = integration["startFromRev"][1:] - if startRev == "none": - startRev = 0 - else: - startRev = int(startRev) - - endRev = integration["endFromRev"][1:] - if endRev == "none": - endRev = 0 - else: - endRev = int(endRev) - - initialBranch = (destRev == 1 and integration["how"] != "branch into") - inRange = (destRev >= startRev and destRev <= endRev) - newer = (destRev > startRev and destRev > endRev) - - if initialBranch or inRange or newer: - integrations.append(integration) - - if deleted: - continue - - if len(integrations) == 0 and integrationCount > 1: - print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) - return False - - return True - - def getUserMap(self): - self.users = {} - - for output in p4CmdList("users"): - if not output.has_key("User"): - continue - self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" - - def run(self, args): - self.branch = "refs/heads/" + self.branch - self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() - if len(self.globalPrefix) != 0: - self.globalPrefix = self.globalPrefix[:-1] - - if len(args) == 0 and len(self.globalPrefix) != 0: - if not self.silent: - print "[using previously specified depot path %s]" % self.globalPrefix - elif len(args) != 1: - return False - else: - if len(self.globalPrefix) != 0 and self.globalPrefix != args[0]: - print "previous import used depot path %s and now %s was specified. this doesn't work!" % (self.globalPrefix, args[0]) - sys.exit(1) - self.globalPrefix = args[0] - - self.changeRange = "" - self.revision = "" - self.users = {} - self.initialParent = "" - self.lastChange = 0 - self.initialTag = "" - - if self.globalPrefix.find("@") != -1: - atIdx = self.globalPrefix.index("@") - self.changeRange = self.globalPrefix[atIdx:] - if self.changeRange == "@all": - self.changeRange = "" - elif self.changeRange.find(",") == -1: - self.revision = self.changeRange - self.changeRange = "" - self.globalPrefix = self.globalPrefix[0:atIdx] - elif self.globalPrefix.find("#") != -1: - hashIdx = self.globalPrefix.index("#") - self.revision = self.globalPrefix[hashIdx:] - self.globalPrefix = self.globalPrefix[0:hashIdx] - elif len(self.previousDepotPath) == 0: - self.revision = "#head" - - if self.globalPrefix.endswith("..."): - self.globalPrefix = self.globalPrefix[:-3] - - if not self.globalPrefix.endswith("/"): - self.globalPrefix += "/" - - self.getUserMap() - - if len(self.changeRange) == 0: - try: - sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % self.branch) - output = sout.read() - if output.endswith("\n"): - output = output[:-1] - tagIdx = output.index(" tags/p4/") - caretIdx = output.find("^") - endPos = len(output) - if caretIdx != -1: - endPos = caretIdx - self.rev = int(output[tagIdx + 9 : endPos]) + 1 - self.changeRange = "@%s,#head" % self.rev - self.initialParent = os.popen("git-rev-parse %s" % self.branch).read()[:-1] - self.initialTag = "p4/%s" % (int(self.rev) - 1) - except: - pass - - self.tz = - time.timezone / 36 - tzsign = ("%s" % self.tz)[0] - if tzsign != '+' and tzsign != '-': - self.tz = "+" + ("%s" % self.tz) - - self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git-fast-import") - - if len(self.revision) > 0: - print "Doing initial import of %s from revision %s" % (self.globalPrefix, self.revision) - - details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = "Initial import of %s from the state at revision %s" % (self.globalPrefix, self.revision) - details["change"] = self.revision - newestRevision = 0 - - fileCnt = 0 - for info in p4CmdList("files %s...%s" % (self.globalPrefix, self.revision)): - change = int(info["change"]) - if change > newestRevision: - newestRevision = change - - if info["action"] == "delete": - fileCnt = fileCnt + 1 - continue - - for prop in [ "depotFile", "rev", "action", "type" ]: - details["%s%s" % (prop, fileCnt)] = info[prop] - - fileCnt = fileCnt + 1 - - details["change"] = newestRevision - - try: - self.commit(details, self.extractFilesFromCommit(details), self.branch, self.globalPrefix) - except IOError: - print self.gitError.read() - - else: - changes = [] - - if len(self.changesFile) > 0: - output = open(self.changesFile).readlines() - changeSet = Set() - for line in output: - changeSet.add(int(line)) - - for change in changeSet: - changes.append(change) - - changes.sort() - else: - output = os.popen("p4 changes %s...%s" % (self.globalPrefix, self.changeRange)).readlines() - - for line in output: - changeNum = line.split(" ")[1] - changes.append(changeNum) - - changes.reverse() - - if len(changes) == 0: - if not self.silent: - print "no changes to import!" - sys.exit(1) - - cnt = 1 - for change in changes: - description = p4Cmd("describe %s" % change) - - if not self.silent: - sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) - sys.stdout.flush() - cnt = cnt + 1 - - try: - files = self.extractFilesFromCommit(description) - if self.detectBranches: - for branch in self.branchesForCommit(files): - self.knownBranches.add(branch) - branchPrefix = self.globalPrefix + branch + "/" - - filesForCommit = self.extractFilesInCommitToBranch(files, branchPrefix) - - merged = "" - parent = "" - ########### remove cnt!!! - if branch not in self.createdBranches and cnt > 2: - self.createdBranches.add(branch) - parent = self.findBranchParent(branchPrefix, files) - if parent == branch: - parent = "" - # elif len(parent) > 0: - # print "%s branched off of %s" % (branch, parent) - - if len(parent) == 0: - merged = self.findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) - if len(merged) > 0: - print "change %s could be a merge from %s into %s" % (description["change"], merged, branch) - if not self.changeIsBranchMerge(merged, branch, int(description["change"])): - merged = "" - - branch = "refs/heads/" + branch - if len(parent) > 0: - parent = "refs/heads/" + parent - if len(merged) > 0: - merged = "refs/heads/" + merged - self.commit(description, files, branch, branchPrefix, parent, merged) - else: - self.commit(description, files, self.branch, self.globalPrefix, self.initialParent) - self.initialParent = "" - except IOError: - print self.gitError.read() - sys.exit(1) - - if not self.silent: - print "" - - self.gitStream.write("reset refs/tags/p4/%s\n" % self.lastChange) - self.gitStream.write("from %s\n\n" % self.branch); - - - self.gitStream.close() - self.gitOutput.close() - self.gitError.close() - - os.popen("git-repo-config p4.depotpath %s" % self.globalPrefix).read() - if len(self.initialTag) > 0: - os.popen("git tag -d %s" % self.initialTag).read() - - return True - -class HelpFormatter(optparse.IndentedHelpFormatter): - def __init__(self): - optparse.IndentedHelpFormatter.__init__(self) - - def format_description(self, description): - if description: - return description + "\n" - else: - return "" - -def printUsage(commands): - print "usage: %s [options]" % sys.argv[0] - print "" - print "valid commands: %s" % ", ".join(commands) - print "" - print "Try %s --help for command specific help." % sys.argv[0] - print "" - -commands = { - "debug" : P4Debug(), - "clean-tags" : P4CleanTags(), - "submit" : P4Sync(), - "sync" : GitSync() -} - -if len(sys.argv[1:]) == 0: - printUsage(commands.keys()) - sys.exit(2) - -cmd = "" -cmdName = sys.argv[1] -try: - cmd = commands[cmdName] -except KeyError: - print "unknown command %s" % cmdName - print "" - printUsage(commands.keys()) - sys.exit(2) - -options = cmd.options -cmd.gitdir = gitdir -options.append(optparse.make_option("--git-dir", dest="gitdir")) - -parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), - options, - description = cmd.description, - formatter = HelpFormatter()) - -(cmd, args) = parser.parse_args(sys.argv[2:], cmd); - -gitdir = cmd.gitdir -if len(gitdir) == 0: - gitdir = ".git" - if not isValidGitDir(gitdir): - cdup = os.popen("git-rev-parse --show-cdup").read()[:-1] - if isValidGitDir(cdup + "/" + gitdir): - os.chdir(cdup) - -if not isValidGitDir(gitdir): - if isValidGitDir(gitdir + "/.git"): - gitdir += "/.git" - else: - die("fatal: cannot locate git repository at %s" % gitdir) - -os.environ["GIT_DIR"] = gitdir - -if not cmd.run(args): - parser.print_help() - diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 4b4fcde72b..5786bffad4 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -1,11 +1,11 @@ -git-p4.py - Perforce <-> Git converter using git-fast-import +git-p4 - Perforce <-> Git converter using git-fast-import Usage ===== git-p4 supports two main modes: Importing from Perforce to a Git repository is -done using "git-p4.py sync". Submitting changes from Git back to Perforce is -done using "git-p4.py submit". +done using "git-p4 sync". Submitting changes from Git back to Perforce is +done using "git-p4 submit". Importing ========= @@ -15,7 +15,7 @@ The procedure is simple: mkdir repo-git cd repo-git git init - git-p4.py sync //path/in/your/perforce/depot + git-p4 sync //path/in/your/perforce/depot This will import the current head revision of the specified depot path into the master branch of your git repository. You can use the --branch=mybranch option @@ -23,7 +23,7 @@ to let git-p4 import from Perforce into a git branch of your choice. If you want to import the entire history of a given depot path just use - git-p4.py sync //path/in/depot@all + git-p4 sync //path/in/depot@all To achieve optimal compression you may want to run 'git repack -a -d -f' after a big import. This may take a while. @@ -56,7 +56,7 @@ back to a Perforce depot. This requires a Perforce checkout separate to your git repository. This is the basic procedure: cd path/to/your/perforce/checkout - git-p4.py submit --git-dir=/path/to/your/git/repository + git-p4 submit --git-dir=/path/to/your/git/repository This will create a temporary git branch, use git-rev-list to find out which git commits are in your current branch but not in the "origin" branch. You can @@ -73,7 +73,7 @@ submit template using "e". If a submit fails you may have to "p4 resolve" and submit manually. You can continue importing the remaining changes with - git-p4.py submit --git-dir=/path/to/your/git/repository --continue + git-p4 submit --git-dir=/path/to/your/git/repository --continue After submitting you should sync your origin branch from Perforce using git-p4's sync command. -- cgit v1.2.3 From a559b289bd78d5a3fac0f908be7d5ff92ad09dcb Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 20:27:41 +0100 Subject: Changed the format of the imported log message slightly, so that it's easier to parse again. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 06858844e5..a5b6d94d1a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -506,7 +506,7 @@ class GitSync(Command): self.gitStream.write("data < 0: -- cgit v1.2.3 From f5816a5522763f46e075cc618eab12110107a917 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 20:36:28 +0100 Subject: Changed the default branch for imports from "master" to "p4" Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- contrib/fast-import/git-p4.txt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a5b6d94d1a..669cf95d74 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -352,7 +352,7 @@ class GitSync(Command): self.knownBranches = Set() self.createdBranches = Set() self.committedChanges = Set() - self.branch = "master" + self.branch = "p4" self.detectBranches = False self.changesFile = "" diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 5786bffad4..0d30aff64c 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -17,9 +17,9 @@ The procedure is simple: git init git-p4 sync //path/in/your/perforce/depot -This will import the current head revision of the specified depot path into the -master branch of your git repository. You can use the --branch=mybranch option -to let git-p4 import from Perforce into a git branch of your choice. +This will import the current head revision of the specified depot path into a +"p4" branch of your git repository. You can use the --branch=mybranch option +to use a different branch. If you want to import the entire history of a given depot path just use -- cgit v1.2.3 From 6ae8de88f53f92dd593c5d03b67d0194e7d4eefe Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 21:10:25 +0100 Subject: Added some helper function(s) to parse the depot path and change number from the log message Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 48 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 669cf95d74..6ead1c4173 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -52,12 +52,44 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) +def extractLogMessageFromGitCommit(commit): + logMessage = "" + foundTitle = False + for log in os.popen("git-cat-file commit %s" % commit).readlines(): + if not foundTitle: + if len(log) == 1: + foundTitle = 1 + continue + + logMessage += log + return logMessage + +def extractDepotPathAndChangeFromGitLog(log): + values = {} + for line in log.split("\n"): + line = line.strip() + if line.startswith("[git-p4:") and line.endswith("]"): + line = line[8:-1].strip() + for assignment in line.split(":"): + variable = assignment.strip() + value = "" + equalPos = assignment.find("=") + if equalPos != -1: + variable = assignment[:equalPos].strip() + value = assignment[equalPos + 1:].strip() + if value.startswith("\"") and value.endswith("\""): + value = value[1:-1] + values[variable] = value + + return values.get("depot-path"), values.get("change") + class Command: def __init__(self): self.usage = "usage: %prog [options]" class P4Debug(Command): def __init__(self): + Command.__init__(self) self.options = [ ] self.description = "A tool to debug the output of p4 -G." @@ -208,17 +240,9 @@ class P4Sync(Command): system("p4 revert %s" % f) system("p4 delete %s" % f) - logMessage = "" - foundTitle = False - for log in os.popen("git-cat-file commit %s" % id).readlines(): - if not foundTitle: - if len(log) == 1: - foundTitle = 1 - continue - - if len(logMessage) > 0: - logMessage += "\t" - logMessage += log + logMessage = extractLogMessageFromGitCommit(id) + logMessage = logMessage.replace("\n", "\n\t") + logMessage = logMessage[:-1] template = os.popen("p4 change -o").read() @@ -506,7 +530,7 @@ class GitSync(Command): self.gitStream.write("data < 0: -- cgit v1.2.3 From 8136a6399c886dec8d1b806a7d8728324e906729 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 21:27:14 +0100 Subject: Helper function to check the existance of a revision Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6ead1c4173..b21dee3199 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -83,6 +83,9 @@ def extractDepotPathAndChangeFromGitLog(log): return values.get("depot-path"), values.get("change") +def gitBranchExists(branch): + return os.system("git-rev-parse %s 2>/dev/null >/dev/null") == 0 + class Command: def __init__(self): self.usage = "usage: %prog [options]" -- cgit v1.2.3 From 569d1bd4092aacd44a826852e087d0f0e9928ce8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 21:34:16 +0100 Subject: Set the default branch in run, not in the constructor Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b21dee3199..e6a34f4f47 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -379,7 +379,7 @@ class GitSync(Command): self.knownBranches = Set() self.createdBranches = Set() self.committedChanges = Set() - self.branch = "p4" + self.branch = "" self.detectBranches = False self.changesFile = "" @@ -706,6 +706,9 @@ class GitSync(Command): self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" def run(self, args): + if len(self.branch) == 0: + self.branch = "p4" + self.branch = "refs/heads/" + self.branch self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() if len(self.globalPrefix) != 0: -- cgit v1.2.3 From 179caebff4a917dc35c8166ec183bc5c76df53e1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 22 Mar 2007 22:17:42 +0100 Subject: Brand new smart incremental import that doesn't need tags or git repo-config :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 38 +++++++++++++++++++++++++++++++------- contrib/fast-import/git-p4.txt | 6 ++---- 2 files changed, 33 insertions(+), 11 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e6a34f4f47..8684e4b20f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -84,7 +84,9 @@ def extractDepotPathAndChangeFromGitLog(log): return values.get("depot-path"), values.get("change") def gitBranchExists(branch): - return os.system("git-rev-parse %s 2>/dev/null >/dev/null") == 0 + if os.system("git-rev-parse %s 2>/dev/null >/dev/null" % branch) == 0: + return True + return False class Command: def __init__(self): @@ -706,17 +708,40 @@ class GitSync(Command): self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" def run(self, args): + self.globalPrefix = "" + self.changeRange = "" + self.initialParent = "" + self.tagLastChange = True + if len(self.branch) == 0: self.branch = "p4" + if len(args) == 0: + if not gitBranchExists(self.branch) and gitBranchExists("origin"): + if not self.silent: + print "Creating %s branch in git repository based on origin" % self.branch + system("git branch %s origin" % self.branch) + + [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) + if len(self.previousDepotPath) > 0 and len(p4Change) > 0: + p4Change = int(p4Change) + 1 + self.globalPrefix = self.previousDepotPath + self.changeRange = "@%s,#head" % p4Change + self.initialParent = self.branch + self.tagLastChange = False + if not self.silent: + print "Performing incremental import into %s git branch" % self.branch self.branch = "refs/heads/" + self.branch - self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() + + if len(self.globalPrefix) == 0: + self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() + if len(self.globalPrefix) != 0: self.globalPrefix = self.globalPrefix[:-1] if len(args) == 0 and len(self.globalPrefix) != 0: if not self.silent: - print "[using previously specified depot path %s]" % self.globalPrefix + print "Depot path: %s" % self.globalPrefix elif len(args) != 1: return False else: @@ -725,10 +750,8 @@ class GitSync(Command): sys.exit(1) self.globalPrefix = args[0] - self.changeRange = "" self.revision = "" self.users = {} - self.initialParent = "" self.lastChange = 0 self.initialTag = "" @@ -890,8 +913,9 @@ class GitSync(Command): if not self.silent: print "" - self.gitStream.write("reset refs/tags/p4/%s\n" % self.lastChange) - self.gitStream.write("from %s\n\n" % self.branch); + if self.tagLastChange: + self.gitStream.write("reset refs/tags/p4/%s\n" % self.lastChange) + self.gitStream.write("from %s\n\n" % self.branch); self.gitStream.close() diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 0d30aff64c..4319c82dcd 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -38,11 +38,9 @@ Incremental Imports After an initial import you can easily synchronize your git repository with newer changes from the Perforce depot by just calling - git-p4.p4 sync + git-p4 sync -in your git repository. git-p4 stores the depot path of the original import in -the .git/config file and remembers the last imported p4 revision as a git tag -called p4/ . +in your git repository. It is recommended to run 'git repack -a -d -f' from time to time when using incremental imports to optimally combine the individual git packs that each -- cgit v1.2.3 From 9512497bcf574a2f70e43be0bcb58e35fb6aaba8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 23 Mar 2007 09:16:07 +0100 Subject: Make it possible to run git-p4 submit from within the git repository Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 38 +++++++++++++++++++++++++++++++++++++- contrib/fast-import/git-p4.txt | 27 +++++++++++++-------------- 2 files changed, 50 insertions(+), 15 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 8684e4b20f..a8f7cce25d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -162,7 +162,7 @@ class P4Sync(Command): self.dryRun = False self.substFile = "" self.firstTime = True - self.origin = "origin" + self.origin = "" self.master = "" self.applyAsPatch = True @@ -304,6 +304,42 @@ class P4Sync(Command): print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) def run(self, args): + global gitdir + # make gitdir absolute so we can cd out into the perforce checkout + gitdir = os.path.abspath(gitdir) + os.environ["GIT_DIR"] = gitdir + depotPath = "" + if gitBranchExists("p4"): + [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) + if len(depotPath) == 0 and gitBranchExists("origin"): + [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) + + if len(depotPath) == 0: + print "Internal error: cannot locate perforce depot path from existing branches" + sys.exit(128) + + if not depotPath.endswith("/"): + depotPath += "/" + clientPath = p4Cmd("where %s..." % depotPath).get("path") + if clientPath.endswith("..."): + clientPath = clientPath[:-3] + + if len(clientPath) == 0: + print "Error: Cannot locate perforce checkout of %s in client view" % depotPath + sys.exit(128) + + print "Perforce checkout for depot path %s located at %s" % (depotPath, clientPath) + os.chdir(clientPath) + response = raw_input("Do you want to sync %s with p4 sync? (y/n)" % clientPath) + if response == "y" or response == "yes": + system("p4 sync ...") + + if len(self.origin) == 0: + if gitBranchExists("p4"): + self.origin = "p4" + else: + self.origin = "origin" + if self.reset: self.firstTime = True diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 4319c82dcd..8bf0805c74 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -51,28 +51,27 @@ Submitting git-p4 has EXPERIMENTAL support for submitting changes from a git repository back to a Perforce depot. This requires a Perforce checkout separate to your -git repository. This is the basic procedure: +git repository. All it should take is calling - cd path/to/your/perforce/checkout - git-p4 submit --git-dir=/path/to/your/git/repository + git-p4 submit -This will create a temporary git branch, use git-rev-list to find out which git -commits are in your current branch but not in the "origin" branch. You can -override the name of the "origin" branch by using the --origin=mybranch option. -The "origin" branch has to be the branch populated with git-p4's sync -operation. +in your git repository. This will attempt to locate the perforce checkout +corresponding to your imported depot path. By default the changes between your +current branch and the "p4" branch will be submitted. If there is no "p4" +branch the "origin" branch will be used as reference instead. You can override +this with the --origin=mysourcebranch option. The "origin" branch has to be the +branch populated with git-p4's sync operation. After some preparations (which might take a while) git-p4 enters a loop where it will first show a Perforce submit template and a diff of the change to -apply. After quitting the pager with 'q' git-p4 asks for confirmation for -issuing the "p4 submit" command and also gives you the option of editing the -submit template using "e". +apply in the editor. After saving and exiting the editor you will be asked whether +you really want to submit the change or not. If a submit fails you may have to "p4 resolve" and submit manually. You can continue importing the remaining changes with - git-p4 submit --git-dir=/path/to/your/git/repository --continue + git-p4 submit --continue -After submitting you should sync your origin branch from Perforce using -git-p4's sync command. +After submitting you should sync your perforce import branch ("p4" or "origin") +from Perforce using git-p4's sync command. -- cgit v1.2.3 From 967f72e21b9f38725d64a7520bca99bbef9e8ab3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 23 Mar 2007 09:30:41 +0100 Subject: Use the new incremental import style by default Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a8f7cce25d..61978c3da6 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -751,21 +751,22 @@ class GitSync(Command): if len(self.branch) == 0: self.branch = "p4" - if len(args) == 0: - if not gitBranchExists(self.branch) and gitBranchExists("origin"): - if not self.silent: - print "Creating %s branch in git repository based on origin" % self.branch - system("git branch %s origin" % self.branch) - - [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) - if len(self.previousDepotPath) > 0 and len(p4Change) > 0: - p4Change = int(p4Change) + 1 - self.globalPrefix = self.previousDepotPath - self.changeRange = "@%s,#head" % p4Change - self.initialParent = self.branch - self.tagLastChange = False - if not self.silent: - print "Performing incremental import into %s git branch" % self.branch + + if len(args) == 0: + if not gitBranchExists(self.branch) and gitBranchExists("origin"): + if not self.silent: + print "Creating %s branch in git repository based on origin" % self.branch + system("git branch %s origin" % self.branch) + + [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) + if len(self.previousDepotPath) > 0 and len(p4Change) > 0: + p4Change = int(p4Change) + 1 + self.globalPrefix = self.previousDepotPath + self.changeRange = "@%s,#head" % p4Change + self.initialParent = self.branch + self.tagLastChange = False + if not self.silent: + print "Performing incremental import into %s git branch" % self.branch self.branch = "refs/heads/" + self.branch -- cgit v1.2.3 From cb2c9db507cda6804f85ebbacb58a7458ab127a6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 24 Mar 2007 09:15:11 +0100 Subject: Different versions of p4 have different output for the where command ;( Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 61978c3da6..9503786207 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -36,6 +36,22 @@ def p4Cmd(cmd): result.update(entry) return result; +def p4Where(depotPath): + if not depotPath.endswith("/"): + depotPath += "/" + output = p4Cmd("where %s..." % depotPath) + clientPath = "" + if "path" in output: + clientPath = output.get("path") + elif "data" in output: + data = output.get("data") + lastSpace = data.rfind(" ") + clientPath = data[lastSpace + 1:] + + if clientPath.endswith("..."): + clientPath = clientPath[:-3] + return clientPath + def die(msg): sys.stderr.write(msg + "\n") sys.exit(1) @@ -318,11 +334,7 @@ class P4Sync(Command): print "Internal error: cannot locate perforce depot path from existing branches" sys.exit(128) - if not depotPath.endswith("/"): - depotPath += "/" - clientPath = p4Cmd("where %s..." % depotPath).get("path") - if clientPath.endswith("..."): - clientPath = clientPath[:-3] + clientPath = p4Where(depotPath) if len(clientPath) == 0: print "Error: Cannot locate perforce checkout of %s in client view" % depotPath -- cgit v1.2.3 From 274917a3d65109cfdf225615177fe27624d8461a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 24 Mar 2007 09:18:20 +0100 Subject: Minor cosmetic fixlet for the git-p4 submit sync question. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 9503786207..aaa5d5ee5f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -342,7 +342,7 @@ class P4Sync(Command): print "Perforce checkout for depot path %s located at %s" % (depotPath, clientPath) os.chdir(clientPath) - response = raw_input("Do you want to sync %s with p4 sync? (y/n)" % clientPath) + response = raw_input("Do you want to sync %s with p4 sync? (y/n) " % clientPath) if response == "y" or response == "yes": system("p4 sync ...") -- cgit v1.2.3 From 9863f4055e1219904f6d26c2803a72fc6c8ce561 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 24 Mar 2007 16:35:05 +0100 Subject: Prefer git command over git-command. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index aaa5d5ee5f..09990be373 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -57,7 +57,7 @@ def die(msg): sys.exit(1) def currentGitBranch(): - return os.popen("git-name-rev HEAD").read().split(" ")[1][:-1] + return os.popen("git name-rev HEAD").read().split(" ")[1][:-1] def isValidGitDir(path): if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): @@ -71,7 +71,7 @@ def system(cmd): def extractLogMessageFromGitCommit(commit): logMessage = "" foundTitle = False - for log in os.popen("git-cat-file commit %s" % commit).readlines(): + for log in os.popen("git cat-file commit %s" % commit).readlines(): if not foundTitle: if len(log) == 1: foundTitle = 1 @@ -100,7 +100,7 @@ def extractDepotPathAndChangeFromGitLog(log): return values.get("depot-path"), values.get("change") def gitBranchExists(branch): - if os.system("git-rev-parse %s 2>/dev/null >/dev/null" % branch) == 0: + if os.system("git rev-parse %s 2>/dev/null >/dev/null" % branch) == 0: return True return False @@ -130,7 +130,7 @@ class P4CleanTags(Command): def run(self, args): branch = currentGitBranch() print "Cleaning out stale p4 import tags..." - sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % branch) + sout, sin, serr = popen2.popen3("git name-rev --tags `git rev-parse %s`" % branch) output = sout.read() try: tagIdx = output.index(" tags/p4/") @@ -195,7 +195,7 @@ class P4Sync(Command): die("Cannot start sync. Previous sync config found at %s" % self.configFile) commits = [] - for line in os.popen("git-rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): + for line in os.popen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): commits.append(line[:-1]) commits.reverse() @@ -229,7 +229,7 @@ class P4Sync(Command): return result def apply(self, id): - print "Applying %s" % (os.popen("git-log --max-count=1 --pretty=oneline %s" % id).read()) + print "Applying %s" % (os.popen("git log --max-count=1 --pretty=oneline %s" % id).read()) diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() filesToAdd = set() filesToDelete = set() @@ -250,9 +250,9 @@ class P4Sync(Command): die("unknown modifier %s for %s" % (modifier, path)) if self.applyAsPatch: - system("git-diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) + system("git diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) else: - system("git-diff-files --name-only -z | git-update-index --remove -z --stdin") + system("git diff-files --name-only -z | git update-index --remove -z --stdin") system("git cherry-pick --no-commit \"%s\"" % id) for f in filesToAdd: @@ -783,7 +783,7 @@ class GitSync(Command): self.branch = "refs/heads/" + self.branch if len(self.globalPrefix) == 0: - self.globalPrefix = self.previousDepotPath = os.popen("git-repo-config --get p4.depotpath").read() + self.globalPrefix = self.previousDepotPath = os.popen("git repo-config --get p4.depotpath").read() if len(self.globalPrefix) != 0: self.globalPrefix = self.globalPrefix[:-1] @@ -830,7 +830,7 @@ class GitSync(Command): if len(self.changeRange) == 0: try: - sout, sin, serr = popen2.popen3("git-name-rev --tags `git-rev-parse %s`" % self.branch) + sout, sin, serr = popen2.popen3("git name-rev --tags `git rev-parse %s`" % self.branch) output = sout.read() if output.endswith("\n"): output = output[:-1] @@ -841,7 +841,7 @@ class GitSync(Command): endPos = caretIdx self.rev = int(output[tagIdx + 9 : endPos]) + 1 self.changeRange = "@%s,#head" % self.rev - self.initialParent = os.popen("git-rev-parse %s" % self.branch).read()[:-1] + self.initialParent = os.popen("git rev-parse %s" % self.branch).read()[:-1] self.initialTag = "p4/%s" % (int(self.rev) - 1) except: pass @@ -851,7 +851,7 @@ class GitSync(Command): if tzsign != '+' and tzsign != '-': self.tz = "+" + ("%s" % self.tz) - self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git-fast-import") + self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git fast-import") if len(self.revision) > 0: print "Doing initial import of %s from revision %s" % (self.globalPrefix, self.revision) @@ -971,7 +971,7 @@ class GitSync(Command): self.gitOutput.close() self.gitError.close() - os.popen("git-repo-config p4.depotpath %s" % self.globalPrefix).read() + os.popen("git repo-config p4.depotpath %s" % self.globalPrefix).read() if len(self.initialTag) > 0: os.popen("git tag -d %s" % self.initialTag).read() @@ -1031,7 +1031,7 @@ gitdir = cmd.gitdir if len(gitdir) == 0: gitdir = ".git" if not isValidGitDir(gitdir): - cdup = os.popen("git-rev-parse --show-cdup").read()[:-1] + cdup = os.popen("git rev-parse --show-cdup").read()[:-1] if isValidGitDir(cdup + "/" + gitdir): os.chdir(cdup) -- cgit v1.2.3 From e20a9e530a02fefaec31b484bd224784b8814554 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Mar 2007 00:13:51 +0200 Subject: Don't try to parse any options with git-p4 debug but pass it straight on to p4 Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 09990be373..5b023b1b7a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1018,14 +1018,18 @@ except KeyError: options = cmd.options cmd.gitdir = gitdir -options.append(optparse.make_option("--git-dir", dest="gitdir")) -parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), - options, - description = cmd.description, - formatter = HelpFormatter()) +args = sys.argv[2:] -(cmd, args) = parser.parse_args(sys.argv[2:], cmd); +if len(options) > 0: + options.append(optparse.make_option("--git-dir", dest="gitdir")) + + parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), + options, + description = cmd.description, + formatter = HelpFormatter()) + + (cmd, args) = parser.parse_args(sys.argv[2:], cmd); gitdir = cmd.gitdir if len(gitdir) == 0: -- cgit v1.2.3 From 8910ac0e888daeefdbe6f7391bece150b12b1ad0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Mar 2007 08:18:55 +0200 Subject: git-p4 debug doesn't need a git repository Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 5b023b1b7a..eb5b40aa98 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -107,6 +107,7 @@ def gitBranchExists(branch): class Command: def __init__(self): self.usage = "usage: %prog [options]" + self.needsGit = True class P4Debug(Command): def __init__(self): @@ -114,6 +115,7 @@ class P4Debug(Command): self.options = [ ] self.description = "A tool to debug the output of p4 -G." + self.needsGit = False def run(self, args): for output in p4CmdList(" ".join(args)): @@ -1031,21 +1033,22 @@ if len(options) > 0: (cmd, args) = parser.parse_args(sys.argv[2:], cmd); -gitdir = cmd.gitdir -if len(gitdir) == 0: - gitdir = ".git" - if not isValidGitDir(gitdir): - cdup = os.popen("git rev-parse --show-cdup").read()[:-1] - if isValidGitDir(cdup + "/" + gitdir): - os.chdir(cdup) +if cmd.needsGit: + gitdir = cmd.gitdir + if len(gitdir) == 0: + gitdir = ".git" + if not isValidGitDir(gitdir): + cdup = os.popen("git rev-parse --show-cdup").read()[:-1] + if isValidGitDir(cdup + "/" + gitdir): + os.chdir(cdup) -if not isValidGitDir(gitdir): - if isValidGitDir(gitdir + "/.git"): - gitdir += "/.git" - else: - die("fatal: cannot locate git repository at %s" % gitdir) + if not isValidGitDir(gitdir): + if isValidGitDir(gitdir + "/.git"): + gitdir += "/.git" + else: + die("fatal: cannot locate git repository at %s" % gitdir) -os.environ["GIT_DIR"] = gitdir + os.environ["GIT_DIR"] = gitdir if not cmd.run(args): parser.print_help() -- cgit v1.2.3 From 1f4ba1cbfc1d08a9c120012c9199caaec3dc5f58 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 26 Mar 2007 22:34:34 +0200 Subject: Added support for mapping p4 labels to git tags Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 58 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index eb5b40aa98..eab5990548 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -625,7 +625,47 @@ class GitSync(Command): self.gitStream.write("\n") - self.lastChange = int(details["change"]) + change = int(details["change"]) + + self.lastChange = change + + if change in self.labels: + label = self.labels[change] + labelDetails = label[0] + labelRevisions = label[1] + + files = p4CmdList("files %s...@%s" % (branchPrefix, change)) + + if len(files) == len(labelRevisions): + + cleanedFiles = {} + for info in files: + if info["action"] == "delete": + continue + cleanedFiles[info["depotFile"]] = info["rev"] + + if cleanedFiles == labelRevisions: + self.gitStream.write("tag tag_%s\n" % labelDetails["label"]) + self.gitStream.write("from %s\n" % branch) + + owner = labelDetails["Owner"] + tagger = "" + if author in self.users: + tagger = "%s %s %s" % (self.users[owner], epoch, self.tz) + else: + tagger = "%s %s %s" % (owner, epoch, self.tz) + self.gitStream.write("tagger %s\n" % tagger) + self.gitStream.write("data <" + def getLabels(self): + self.labels = {} + + for output in p4CmdList("labels %s..." % self.globalPrefix): + label = output["label"] + revisions = {} + newestChange = 0 + for file in p4CmdList("files //...@%s" % label): + revisions[file["depotFile"]] = file["rev"] + change = int(file["change"]) + if change > newestChange: + newestChange = change + + self.labels[newestChange] = [output, revisions] + def run(self, args): self.globalPrefix = "" self.changeRange = "" @@ -829,6 +884,7 @@ class GitSync(Command): self.globalPrefix += "/" self.getUserMap() + self.getLabels(); if len(self.changeRange) == 0: try: -- cgit v1.2.3 From a46668faf7f02c3fb55bf877e16c7ffdf8c9a129 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 28 Mar 2007 17:05:38 +0200 Subject: Fix variable usage in tag import Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index eab5990548..60c4b3dc6c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -660,11 +660,11 @@ class GitSync(Command): self.gitStream.write("EOT\n\n") else: - if not silent: + if not self.silent: print "Tag %s does not match with change %s: files do not match." % (labelDetails["label"], change) else: - if not silent: + if not self.silent: print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change) def extractFilesInCommitToBranch(self, files, branchPrefix): -- cgit v1.2.3 From c9b50e6307aa9931e18b6cd4d00de817b8e4a4c2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 29 Mar 2007 19:15:24 +0200 Subject: Fix the docs for git-p4 submit and turn git-p4 submit --master=foo into simply git-p4 submit mytopicbranch. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 18 +++++++++++------- contrib/fast-import/git-p4.txt | 23 +++++++++-------------- 2 files changed, 20 insertions(+), 21 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 60c4b3dc6c..59c3edae19 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -167,13 +167,13 @@ class P4Sync(Command): optparse.make_option("--continue", action="store_false", dest="firstTime"), optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), - optparse.make_option("--master", dest="master"), optparse.make_option("--log-substitutions", dest="substFile"), optparse.make_option("--noninteractive", action="store_false"), optparse.make_option("--dry-run", action="store_true"), optparse.make_option("--apply-as-patch", action="store_true", dest="applyAsPatch") ] self.description = "Submit changes from git to the perforce depot." + self.usage += " [name of git branch to submit into perforce depot]" self.firstTime = True self.reset = False self.interactive = True @@ -181,7 +181,6 @@ class P4Sync(Command): self.substFile = "" self.firstTime = True self.origin = "" - self.master = "" self.applyAsPatch = True self.logSubstitutions = {} @@ -326,6 +325,16 @@ class P4Sync(Command): # make gitdir absolute so we can cd out into the perforce checkout gitdir = os.path.abspath(gitdir) os.environ["GIT_DIR"] = gitdir + + if len(args) == 0: + self.master = currentGitBranch() + if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)): + die("Detecting current git branch failed!") + elif len(args) == 1: + self.master = args[0] + else: + return False + depotPath = "" if gitBranchExists("p4"): [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) @@ -362,11 +371,6 @@ class P4Sync(Command): tokens = line[:-1].split("=") self.logSubstitutions[tokens[0]] = tokens[1] - if len(self.master) == 0: - self.master = currentGitBranch() - if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)): - die("Detecting current git branch failed!") - self.check() self.configFile = gitdir + "/p4-git-sync.cfg" self.config = shelve.open(self.configFile, writeback=True) diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 8bf0805c74..30e2cb9a55 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -49,23 +49,19 @@ incremental import creates through the use of git-fast-import. Submitting ========== -git-p4 has EXPERIMENTAL support for submitting changes from a git repository -back to a Perforce depot. This requires a Perforce checkout separate to your -git repository. All it should take is calling +git-p4 has support for submitting changes from a git repository back to the +Perforce depot. This requires a Perforce checkout separate to your git +repository. To submit all changes that are in the current git branch but not in +the "p4" branch (or "origin" if "p4" doesn't exist) simply call git-p4 submit -in your git repository. This will attempt to locate the perforce checkout -corresponding to your imported depot path. By default the changes between your -current branch and the "p4" branch will be submitted. If there is no "p4" -branch the "origin" branch will be used as reference instead. You can override -this with the --origin=mysourcebranch option. The "origin" branch has to be the -branch populated with git-p4's sync operation. +in your git repository. If you want to submit changes in a specific branch that +is not your current git branch you can also pass that as an argument: -After some preparations (which might take a while) git-p4 enters a loop where -it will first show a Perforce submit template and a diff of the change to -apply in the editor. After saving and exiting the editor you will be asked whether -you really want to submit the change or not. + git-p4 submit mytopicbranch + +You can override the reference branch with the --origin=mysourcebranch option. If a submit fails you may have to "p4 resolve" and submit manually. You can continue importing the remaining changes with @@ -74,4 +70,3 @@ continue importing the remaining changes with After submitting you should sync your perforce import branch ("p4" or "origin") from Perforce using git-p4's sync command. - -- cgit v1.2.3 From 2a9489c0249c8aef6c6ead501bae25771e710e12 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 1 Apr 2007 13:39:39 +0200 Subject: Fix "compilation" :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 59c3edae19..0443337359 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -416,7 +416,7 @@ class GitSync(Command): optparse.make_option("--changesfile", dest="changesFile"), optparse.make_option("--silent", dest="silent", action="store_true"), optparse.make_option("--known-branches", dest="knownBranches"), - optparse.make_option("--cache", dest="doCache", action="store_true"), + optparse.make_option("--data-cache", dest="dataCache", action="store_true"), optparse.make_option("--command-cache", dest="commandCache", action="store_true") ] self.description = """Imports from Perforce into a git repository.\n @@ -500,7 +500,7 @@ class GitSync(Command): if knownBranch: continue - for branch in knownBranches: + for branch in self.knownBranches: #if relativePath.startswith(branch): if self.isSubPathOf(relativePath, branch): if len(branches) == 0: -- cgit v1.2.3 From 711544b00c22b1c2559333e8925e449812f9e5cf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 1 Apr 2007 15:40:46 +0200 Subject: Clean up python class names. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 0443337359..24c8e66e87 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -160,7 +160,7 @@ class P4CleanTags(Command): print "%s tags removed." % len(allTags) return True -class P4Sync(Command): +class P4Submit(Command): def __init__(self): Command.__init__(self) self.options = [ @@ -407,7 +407,7 @@ class P4Sync(Command): return True -class GitSync(Command): +class P4Sync(Command): def __init__(self): Command.__init__(self) self.options = [ @@ -1060,8 +1060,8 @@ def printUsage(commands): commands = { "debug" : P4Debug(), "clean-tags" : P4CleanTags(), - "submit" : P4Sync(), - "sync" : GitSync() + "submit" : P4Submit(), + "sync" : P4Sync() } if len(sys.argv[1:]) == 0: -- cgit v1.2.3 From 01ce1fe9676e3f714f3a2d068bf8cfe021f4073c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 7 Apr 2007 23:46:50 +0200 Subject: Added git-p4 rebase convenience Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 22 ++++++++++++++++++++-- contrib/fast-import/git-p4.txt | 14 +++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 24c8e66e87..aa85800d69 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -804,7 +804,11 @@ class P4Sync(Command): def getLabels(self): self.labels = {} - for output in p4CmdList("labels %s..." % self.globalPrefix): + l = p4CmdList("labels %s..." % self.globalPrefix) + if len(l) > 0: + print "Finding files belonging to labels in %s" % self.globalPrefix + + for output in l: label = output["label"] revisions = {} newestChange = 0 @@ -1039,6 +1043,19 @@ class P4Sync(Command): return True +class P4Rebase(Command): + def __init__(self): + Command.__init__(self) + self.options = [ ] + self.description = "Fetches the latest revision from perforce and rebases the current work (branch) against it" + + def run(self, args): + sync = P4Sync() + sync.run([]) + print "Rebasing the current branch" + system("git rebase p4") + return True + class HelpFormatter(optparse.IndentedHelpFormatter): def __init__(self): optparse.IndentedHelpFormatter.__init__(self) @@ -1061,7 +1078,8 @@ commands = { "debug" : P4Debug(), "clean-tags" : P4CleanTags(), "submit" : P4Submit(), - "sync" : P4Sync() + "sync" : P4Sync(), + "rebase" : P4Rebase() } if len(sys.argv[1:]) == 0: diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 30e2cb9a55..5f7251c2d6 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -40,12 +40,24 @@ newer changes from the Perforce depot by just calling git-p4 sync -in your git repository. +in your git repository. By default the "p4" branch is updated. It is recommended to run 'git repack -a -d -f' from time to time when using incremental imports to optimally combine the individual git packs that each incremental import creates through the use of git-fast-import. +Updating +======== + +A common working pattern is to fetch the latest changes from the Perforce depot +and merge them with local uncommitted changes. The recommended way is to use +git's rebase mechanism to preserve linear history. git-p4 provides a convenient + + git-p4 rebase + +command that calls git-p4 sync followed by git rebase to rebase the current +working branch. + Submitting ========== -- cgit v1.2.3 From 1f52af6c732bc17415474c426dc009f9e6a36a83 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 00:07:02 +0200 Subject: Provide a tree summary after git-p4 rebase Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index aa85800d69..170af90abc 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -974,7 +974,7 @@ class P4Sync(Command): if len(changes) == 0: if not self.silent: print "no changes to import!" - sys.exit(1) + return True cnt = 1 for change in changes: @@ -1053,7 +1053,9 @@ class P4Rebase(Command): sync = P4Sync() sync.run([]) print "Rebasing the current branch" + oldHead = os.popen("git rev-parse HEAD").read()[:-1] system("git rebase p4") + system("git diff-tree --stat --summary -M %s HEAD" % oldHead) return True class HelpFormatter(optparse.IndentedHelpFormatter): -- cgit v1.2.3 From cb53e1f8e9a78ab3f980250d2388a62ae2a42d77 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 00:12:02 +0200 Subject: Turn off potentially slow label detection by default Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 170af90abc..fcdfe22a45 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -417,7 +417,8 @@ class P4Sync(Command): optparse.make_option("--silent", dest="silent", action="store_true"), optparse.make_option("--known-branches", dest="knownBranches"), optparse.make_option("--data-cache", dest="dataCache", action="store_true"), - optparse.make_option("--command-cache", dest="commandCache", action="store_true") + optparse.make_option("--command-cache", dest="commandCache", action="store_true"), + optparse.make_option("--detect-labels", dest="detectLabels", action="store_true") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -437,6 +438,7 @@ class P4Sync(Command): self.committedChanges = Set() self.branch = "" self.detectBranches = False + self.detectLabels = False self.changesFile = "" def p4File(self, depotPath): @@ -892,7 +894,9 @@ class P4Sync(Command): self.globalPrefix += "/" self.getUserMap() - self.getLabels(); + self.labels = {} + if self.detectLabels: + self.getLabels(); if len(self.changeRange) == 0: try: -- cgit v1.2.3 From 68ed351ab51c8b5731665fe6a8a7cc9651edf6c9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 09:00:55 +0200 Subject: Honor --silent for labels Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index fcdfe22a45..65660e1351 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -807,7 +807,7 @@ class P4Sync(Command): self.labels = {} l = p4CmdList("labels %s..." % self.globalPrefix) - if len(l) > 0: + if len(l) > 0 and not silent: print "Finding files belonging to labels in %s" % self.globalPrefix for output in l: -- cgit v1.2.3 From f9a3a4f796461276bbbcfef965984086e8e00b46 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 10:08:26 +0200 Subject: Added git-p4 clone convenience command Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 57 ++++++++++++++++++++++++++++++++++++++++-- contrib/fast-import/git-p4.txt | 23 ++++++++++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 65660e1351..0a22d9a2e4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -440,6 +440,7 @@ class P4Sync(Command): self.detectBranches = False self.detectLabels = False self.changesFile = "" + self.tagLastChange = True def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -826,7 +827,6 @@ class P4Sync(Command): self.globalPrefix = "" self.changeRange = "" self.initialParent = "" - self.tagLastChange = True if len(self.branch) == 0: self.branch = "p4" @@ -1062,6 +1062,58 @@ class P4Rebase(Command): system("git diff-tree --stat --summary -M %s HEAD" % oldHead) return True +class P4Clone(P4Sync): + def __init__(self): + P4Sync.__init__(self) + self.description = "Creates a new git repository and imports from Perforce into it" + self.usage = "usage: %prog [options] //depot/path[@revRange] [directory]" + self.needsGit = False + self.tagLastChange = False + + def run(self, args): + if len(args) < 1: + return False + depotPath = args[0] + dir = "" + if len(args) == 2: + dir = args[1] + elif len(args) > 2: + return False + + if not depotPath.startswith("//"): + return False + + if len(dir) == 0: + dir = depotPath + atPos = dir.rfind("@") + if atPos != -1: + dir = dir[0:atPos] + hashPos = dir.rfind("#") + if hashPos != -1: + dir = dir[0:hashPos] + + if dir.endswith("..."): + dir = dir[:-3] + + if dir.endswith("/"): + dir = dir[:-1] + + slashPos = dir.rfind("/") + if slashPos != -1: + dir = dir[slashPos + 1:] + + print "Importing from %s into %s" % (depotPath, dir) + os.makedirs(dir) + os.chdir(dir) + system("git init") + if not P4Sync.run(self, [depotPath]): + return False + os.wait() + if self.branch != "master": + system("git branch master p4") + system("git checkout -f") + return True + class HelpFormatter(optparse.IndentedHelpFormatter): def __init__(self): optparse.IndentedHelpFormatter.__init__(self) @@ -1085,7 +1137,8 @@ commands = { "clean-tags" : P4CleanTags(), "submit" : P4Submit(), "sync" : P4Sync(), - "rebase" : P4Rebase() + "rebase" : P4Rebase(), + "clone" : P4Clone() } if len(sys.argv[1:]) == 0: diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 5f7251c2d6..99ae85bd7b 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -10,7 +10,25 @@ done using "git-p4 submit". Importing ========= -The procedure is simple: +You can simply start with + + git-p4 clone //depot/path/project + +or + + git-p4 clone //depot/path/project myproject + +This will create an empty git repository in a subdirectory called "project" (or +"myproject" with the second command), import the head revision from the +specified perforce path into a git "p4" branch, create a master branch off it +and check it out. If you want the entire history (not just the head revision) then +you can simply append a "@all" to the depot path: + + git-p4 clone //depot/project/main@all myproject + + + +If you want more control you can also use the git-p4 sync command directly: mkdir repo-git cd repo-git @@ -31,6 +49,9 @@ a big import. This may take a while. Support for Perforce integrations is still work in progress. Don't bother trying it unless you want to hack on it :) +For convenience there's also the git-p4 clone command that works similar to +git-clone and combines the creation of the git repository with the the initial +import and the branch setup Incremental Imports =================== -- cgit v1.2.3 From c45b1cfe1eda2cc67c4f07a0cd6986911d7c2fd8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 10:13:32 +0200 Subject: Fix file determination for #head imports Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 0a22d9a2e4..28b088544b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -938,7 +938,8 @@ class P4Sync(Command): newestRevision = change if info["action"] == "delete": - fileCnt = fileCnt + 1 + # don't increase the file cnt, otherwise details["depotFile123"] will have gaps! + #fileCnt = fileCnt + 1 continue for prop in [ "depotFile", "rev", "action", "type" ]: -- cgit v1.2.3 From 10c3211b81a2f67c3853900e462333933f910696 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 10:15:47 +0200 Subject: fix variable usage (oops) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 28b088544b..72fa48af04 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -808,7 +808,7 @@ class P4Sync(Command): self.labels = {} l = p4CmdList("labels %s..." % self.globalPrefix) - if len(l) > 0 and not silent: + if len(l) > 0 and not self.silent: print "Finding files belonging to labels in %s" % self.globalPrefix for output in l: -- cgit v1.2.3 From 7243b350b3469eb78be950331290ea16e57c3de8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 8 Apr 2007 10:21:56 +0200 Subject: Added a simple example of usage to the "documentation" :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 99ae85bd7b..4f6a680f95 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -103,3 +103,25 @@ continue importing the remaining changes with After submitting you should sync your perforce import branch ("p4" or "origin") from Perforce using git-p4's sync command. + + +Example +======= + +# Clone a repository + git-p4 clone //depot/path/project +# Enter the newly cloned directory + cd project +# Do some work... + vi foo.h +# ... and commit locally to gi + git commit foo.h +# In the meantime somebody submitted changes to the Perforce depot. Rebase your latest +# changes against the latest changes in Perforce: + git-p4 rebase +# Submit your locally committed changes back to Perforce + git-p4 submit +# ... and synchronize with Perforce + git-p4 rebase + + -- cgit v1.2.3 From 80b5910fac79b22ccc8eabb2262562c913d5603c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 9 Apr 2007 12:43:40 +0200 Subject: Allow for convenient rebasing after git-p4 submit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 72fa48af04..4fbfaf11b6 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -352,6 +352,7 @@ class P4Submit(Command): sys.exit(128) print "Perforce checkout for depot path %s located at %s" % (depotPath, clientPath) + oldWorkingDirectory = os.getcwd() os.chdir(clientPath) response = raw_input("Do you want to sync %s with p4 sync? (y/n) " % clientPath) if response == "y" or response == "yes": @@ -403,6 +404,11 @@ class P4Submit(Command): print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." system("p4 edit ... >/dev/null") system("p4 revert ... >/dev/null") + response = raw_input("Do you want to sync from Perforce now using git-p4 rebase (y/n)? ") + if response == "y" or response == "yes": + os.chdir(oldWorkingDirectory) + rebase = P4Rebase() + rebase.run([]) os.remove(self.configFile) return True -- cgit v1.2.3 From fd4ca86a0b920ddde464e787ef2350126f083d75 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 13 Apr 2007 22:21:10 +0200 Subject: Print an error message of some sort if git fast-import fails. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 4fbfaf11b6..6db757ae43 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -958,6 +958,7 @@ class P4Sync(Command): try: self.commit(details, self.extractFilesFromCommit(details), self.branch, self.globalPrefix) except IOError: + print "IO error with git fast-import. Is your git version recent enough?" print self.gitError.read() else: -- cgit v1.2.3 From f291b4e3d47fb03a65c0dfcfbd28f8fabb6187c1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 14 Apr 2007 11:21:50 +0200 Subject: Fix the timezone formatting. Now qgit also displays (parses) it correctly. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6db757ae43..44a07c27ce 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -922,10 +922,7 @@ class P4Sync(Command): except: pass - self.tz = - time.timezone / 36 - tzsign = ("%s" % self.tz)[0] - if tzsign != '+' and tzsign != '-': - self.tz = "+" + ("%s" % self.tz) + self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git fast-import") -- cgit v1.2.3 From 8b72ca0f76213eb375840e6b9069b260d97f8286 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 14 Apr 2007 16:05:54 +0200 Subject: Removed the old patch apply code from git-p4 submit. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 44a07c27ce..3202a819ca 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -170,7 +170,6 @@ class P4Submit(Command): optparse.make_option("--log-substitutions", dest="substFile"), optparse.make_option("--noninteractive", action="store_false"), optparse.make_option("--dry-run", action="store_true"), - optparse.make_option("--apply-as-patch", action="store_true", dest="applyAsPatch") ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@ -181,7 +180,6 @@ class P4Submit(Command): self.substFile = "" self.firstTime = True self.origin = "" - self.applyAsPatch = True self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" @@ -202,10 +200,6 @@ class P4Submit(Command): self.config["commits"] = commits - if not self.applyAsPatch: - print "Creating temporary p4-sync branch from %s ..." % self.origin - system("git checkout -f -b p4-sync %s" % self.origin) - def prepareLogMessage(self, template, message): result = "" @@ -250,11 +244,7 @@ class P4Submit(Command): else: die("unknown modifier %s for %s" % (modifier, path)) - if self.applyAsPatch: - system("git diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) - else: - system("git diff-files --name-only -z | git update-index --remove -z --stdin") - system("git cherry-pick --no-commit \"%s\"" % id) + system("git diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) for f in filesToAdd: system("p4 add %s" % f) @@ -397,13 +387,6 @@ class P4Submit(Command): print "No changes found to apply between %s and current HEAD" % self.origin else: print "All changes applied!" - if not self.applyAsPatch: - print "Deleting temporary p4-sync branch and going back to %s" % self.master - system("git checkout %s" % self.master) - system("git branch -D p4-sync") - print "Cleaning out your perforce checkout by doing p4 edit ... ; p4 revert ..." - system("p4 edit ... >/dev/null") - system("p4 revert ... >/dev/null") response = raw_input("Do you want to sync from Perforce now using git-p4 rebase (y/n)? ") if response == "y" or response == "yes": os.chdir(oldWorkingDirectory) -- cgit v1.2.3 From 5e80dd4d7e34fdd507997615d7fdefc2411571eb Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 14 Apr 2007 16:09:43 +0200 Subject: Slightly improved formatting of the raw_input questions. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3202a819ca..9c9852c75f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -277,7 +277,7 @@ class P4Submit(Command): firstIteration = True while response == "e": if not firstIteration: - response = raw_input("Do you want to submit this change (y/e/n)? ") + response = raw_input("Do you want to submit this change? [y]es/[e]dit/[n]o ") firstIteration = False if response == "e": [handle, fileName] = tempfile.mkstemp() @@ -344,7 +344,7 @@ class P4Submit(Command): print "Perforce checkout for depot path %s located at %s" % (depotPath, clientPath) oldWorkingDirectory = os.getcwd() os.chdir(clientPath) - response = raw_input("Do you want to sync %s with p4 sync? (y/n) " % clientPath) + response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % clientPath) if response == "y" or response == "yes": system("p4 sync ...") @@ -387,7 +387,7 @@ class P4Submit(Command): print "No changes found to apply between %s and current HEAD" % self.origin else: print "All changes applied!" - response = raw_input("Do you want to sync from Perforce now using git-p4 rebase (y/n)? ") + response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") if response == "y" or response == "yes": os.chdir(oldWorkingDirectory) rebase = P4Rebase() -- cgit v1.2.3 From 90865adc01213520980459ddc261a019f673cce8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 15 Apr 2007 09:34:15 +0200 Subject: A new attempt at fixing the child-fast-import-process-not-finished race condition in the clone command Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 9c9852c75f..b77cb20e3f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -907,7 +907,10 @@ class P4Sync(Command): self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) - self.gitOutput, self.gitStream, self.gitError = popen2.popen3("git fast-import") + importProcess = popen2.Popen3("git fast-import", capturestderr = True) + self.gitOutput = importProcess.fromchild + self.gitStream = importProcess.tochild + self.gitError = importProcess.childerr if len(self.revision) > 0: print "Doing initial import of %s from revision %s" % (self.globalPrefix, self.revision) @@ -1028,6 +1031,7 @@ class P4Sync(Command): self.gitStream.close() self.gitOutput.close() self.gitError.close() + importProcess.wait() os.popen("git repo-config p4.depotpath %s" % self.globalPrefix).read() if len(self.initialTag) > 0: @@ -1096,7 +1100,6 @@ class P4Clone(P4Sync): system("git init") if not P4Sync.run(self, [depotPath]): return False - os.wait() if self.branch != "master": system("git branch master p4") system("git checkout -f") -- cgit v1.2.3 From 51a2640afdd12475642728dc3576966abe0dba6d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 15 Apr 2007 09:59:56 +0200 Subject: Handle patch errors in git-p4 submit better. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b77cb20e3f..fb13469ce2 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -244,7 +244,33 @@ class P4Submit(Command): else: die("unknown modifier %s for %s" % (modifier, path)) - system("git diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\" | patch -p1" % (id, id)) + diffcmd = "git diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\"" % (id, id) + patchcmd = diffcmd + " | patch -p1" + + if os.system(patchcmd + " --dry-run --silent") != 0: + print "Unfortunately applying the change failed!" + print "What do you want to do?" + response = "x" + while response != "s" and response != "a" and response != "w": + response = raw_input("[s]kip this patch / [a]pply the patch forcibly and with .rej files / [w]rite the patch to a file (patch.txt) ") + if response == "s": + print "Skipping! Good luck with the next patches..." + return + elif response == "a": + os.system(patchcmd) + if len(filesToAdd) > 0: + print "You may also want to call p4 add on the following files:" + print " ".join(filesToAdd) + if len(filesToDelete): + print "The following files should be scheduled for deletion with p4 delete:" + print " ".join(filesToDelete) + die("Please resolve and submit the conflict manually and continue afterwards with git-p4 submit --continue") + elif response == "w": + system(diffcmd + " > patch.txt") + print "Patch saved to patch.txt in %s !" % self.clientPath + die("Please resolve and submit the conflict manually and continue afterwards with git-p4 submit --continue") + + system(patchcmd) for f in filesToAdd: system("p4 add %s" % f) @@ -335,16 +361,16 @@ class P4Submit(Command): print "Internal error: cannot locate perforce depot path from existing branches" sys.exit(128) - clientPath = p4Where(depotPath) + self.clientPath = p4Where(depotPath) - if len(clientPath) == 0: + if len(self.clientPath) == 0: print "Error: Cannot locate perforce checkout of %s in client view" % depotPath sys.exit(128) - print "Perforce checkout for depot path %s located at %s" % (depotPath, clientPath) + print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath) oldWorkingDirectory = os.getcwd() - os.chdir(clientPath) - response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % clientPath) + os.chdir(self.clientPath) + response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % self.clientPath) if response == "y" or response == "yes": system("p4 sync ...") -- cgit v1.2.3 From 9398e5aa1698cdb518f7118e6f6eeebbea7f5e58 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 20 Apr 2007 02:08:47 -0400 Subject: Contribute a fairly paranoid update hook I'm using a variant of this update hook in a corporate environment where we perform some validations of the commits and tags that are being pushed. The model is a "central repository" type setup, where users are given access to push to specific branches within the shared central repository. In this particular installation we run a specially patched git-receive-pack in setuid mode via SSH, allowing all writes into the repository as the repository owner, but only if this hook blesses it. One of the major checks we perform with this hook is that the 'committer' line of a commit, or the 'tagger' line of a new annotated tag actually correlates to the UNIX user who is performing the push. Users can falsify these lines on their local repositories, but the central repository that management trusts will reject all such forgery attempts. Of course 'author' lines are still allowed to be any value, as sometimes changes do come from other individuals. Another nice feature of this hook is the access control lists for all repositories on the system can also be stored and tracked in a supporting Git repository, which can also be access controlled by itself. This allows full auditing of who-had-what-when-and-why, thanks to git-blame's data mining capabilities. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 284 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 contrib/hooks/update-paranoid (limited to 'contrib') 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 + committer = John R. Doe + + [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 () { + 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 () { + 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 () { + 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 () { + 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 $_ = ; 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"; -- cgit v1.2.3 From 413689d36f9b98c57bff9510ac4cdfb40e4ef9fc Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 19 Apr 2007 13:16:58 +0200 Subject: git.el: Add a commit description to the reflog. Add a description of the commit to the reflog using the first line of the log message, the same way the git-commit script does it. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) (limited to 'contrib') 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)) -- cgit v1.2.3 From 2c9750cc8b902a55669183e05533207dd7ec71fd Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Thu, 19 Apr 2007 22:26:03 +0530 Subject: gitview: annotation support List files modifed as a part of the commit in the diff window Support annotation of the file listed in the diff window Support history browsing in the annotation window. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 260 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 256 insertions(+), 4 deletions(-) (limited to 'contrib') 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 """ __copyright__ = "Copyright (C) 2006 Hewlett-Packard Development Company, L.P." -__author__ = "Aneesh Kumar K.V " +__copyright__ = "Copyright (C) 2007 Aneesh Kumar K.V 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 "]) + dialog.set_authors(["Aneesh Kumar K.V "]) 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) -- cgit v1.2.3 From 2122591b3b5c6d93d3052a3151afcfa3146ede84 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Mon, 23 Apr 2007 17:18:16 -0700 Subject: Add clean.requireForce option, and add -f option to git-clean to override it Add a new configuration option clean.requireForce. If set, git-clean will refuse to run, unless forced with the new -f option, or not acting due to -n. Signed-off-by: Josh Triplett Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7c03403484..46356e8a27 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -790,6 +790,7 @@ _git_config () core.legacyHeaders core.packedGitWindowSize core.packedGitLimit + clean.requireForce color.branch color.branch.current color.branch.local -- cgit v1.2.3 From 46f6178a3f4e7a5bf8b47da0f3cc5c998d907ce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 24 Apr 2007 13:51:04 +0200 Subject: fix importing of subversion tars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add a / between the prefix and name fields of the tar archive if prefix is non-empty. Signed-off-by: Uwe Kleine-König Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 5585a8b2c5..184214689d 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -64,7 +64,12 @@ foreach my $tar_file (@ARGV) } print FI "\n"; - my $path = "$prefix$name"; + my $path; + if ($prefix) { + $path = "$prefix/$name"; + } else { + $path = "$name"; + } $files{$path} = [$next_mark++, $mode]; $commit_time = $mtime if $mtime > $commit_time; -- cgit v1.2.3 From 8e404f82abdfd912ad43c6fae7c218ff75a5d122 Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Thu, 26 Apr 2007 22:35:39 +0100 Subject: post-receive-email example hook: fastforward should have been fast_forward Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 65160153ee..edb30f6f0f 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -302,7 +302,7 @@ generate_update_branch_email() # List all of the revisions that were removed by this update, in a fast forward # update, this list will be empty, because rev-list O ^N is empty. For a non # fast forward, O ^N is the list of removed revisions - fastforward="" + fast_forward="" rev="" for rev in $(git rev-list $newrev..$oldrev) do -- cgit v1.2.3 From 024e5b31af6f06d39542ab1a44de358d7734388b Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Thu, 26 Apr 2007 22:36:24 +0100 Subject: post-receive-email example hook: detect rewind-only updates and output sensible message Sometimes a non-fast-forward update doesn't add new commits, it merely removes old commits. This patch adds support for detecting that and outputting a more correct message. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 77 ++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 23 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index edb30f6f0f..e175b426a4 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -327,36 +327,67 @@ generate_update_branch_email() if [ -z "$fastforward" ]; then echo " from $oldrev ($oldrev_type)" else + # 1. Existing revisions were removed. In this case newrev is a + # subset of oldrev - this is the reverse of a fast-forward, + # a rewind + # 2. New revisions were added on top of an old revision, this is + # a rewind and addition. + + # (1) certainly happened, (2) possibly. When (2) hasn't happened, + # we set a flag to indicate that no log printout is required. + echo "" - echo "This update added new revisions after undoing old revisions. That is to" - echo "say, the old revision is not a strict subset of the new revision. This" - echo "situation occurs when you --force push a change and generate a" - echo "repository containing something like this:" - echo "" - echo " * -- * -- B -- O -- O -- O ($oldrev)" - echo " \\" - echo " N -- N -- N ($newrev)" - echo "" - echo "When this happens we assume that you've already had alert emails for all" - echo "of the O revisions, and so we here report only the revisions in the N" - echo "branch from the common base, B." + + # Find the common ancestor of the old and new revisions and compare + # it with newrev + baserev=$(git merge-base $oldrev $newrev) + rewind_only="" + if [ "$baserev" = "$newrev" ]; then + echo "This update discarded existing revisions and left the branch pointing at" + echo "a previous point in the repository history." + echo "" + echo " * -- * -- N ($newrev)" + echo " \\" + echo " O -- O -- O ($oldrev)" + echo "" + echo "The removed revisions are not necessarilly gone - if another reference" + echo "still refers to them they will stay in the repository." + rewind_only=1 + else + echo "This update added new revisions after undoing existing revisions. That is" + echo "to say, the old revision is not a strict subset of the new revision. This" + echo "situation occurs when you --force push a change and generate a repository" + echo "containing something like this:" + echo "" + echo " * -- * -- B -- O -- O -- O ($oldrev)" + echo " \\" + echo " N -- N -- N ($newrev)" + echo "" + echo "When this happens we assume that you've already had alert emails for all" + echo "of the O revisions, and so we here report only the revisions in the N" + echo "branch from the common base, B." + fi fi echo "" - echo "Those revisions listed above that are new to this repository have" - echo "not appeared on any other notification email; so we list those" - echo "revisions in full, below." + if [ -z "$rewind_only" ]; then + echo "Those revisions listed above that are new to this repository have" + echo "not appeared on any other notification email; so we list those" + echo "revisions in full, below." - echo "" - echo $LOGBEGIN - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $oldrev..$newrev + echo "" + echo $LOGBEGIN + git rev-parse --not --branches | grep -v $(git rev-parse $refname) | + git rev-list --pretty --stdin $oldrev..$newrev - # XXX: Need a way of detecting whether git rev-list actually outputted - # anything, so that we can issue a "no new revisions added by this - # update" message + # XXX: Need a way of detecting whether git rev-list actually outputted + # anything, so that we can issue a "no new revisions added by this + # update" message - echo $LOGEND + echo $LOGEND + else + echo "No new revisions were added by this update." + fi # The diffstat is shown from the old revision to the new revision. This # is to show the truth of what happened in this change. There's no point -- cgit v1.2.3 From c855195cd014f8b5a32a32b36cb77319654583a3 Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Thu, 26 Apr 2007 22:37:16 +0100 Subject: post-receive-email example hook: sed command for getting description was wrong The sed command that extracted the first line of the project description didn't include the -n switch and hence the project name was being printed twice. This was ruining the email header generation because it was assumed that the description was only one line and was included in the subject. This turned the subject into a two line item and prematurely finished the header. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index e175b426a4..d1bef9125b 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -587,7 +587,7 @@ if [ -z "$GIT_DIR" ]; then exit 1 fi -projectdesc=$(sed -e '1p' "$GIT_DIR/description") +projectdesc=$(sed -ne '1p' "$GIT_DIR/description") # Check if the description is unchanged from it's default, and shorten it to a # more manageable length if it is if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null -- cgit v1.2.3 From 87859f34434dda61cabb03447efd1dd2fe7ebac7 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 29 Apr 2007 01:59:47 +0200 Subject: import-tars: be nice to wrong directory modes Some tars seem to have modes 0755 for directories, not 01000755. Do not generate an empty object for them, but ignore them. Noticed by riddochc on IRC. Signed-off-by: Johannes Schindelin Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 5585a8b2c5..e84647770a 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -52,6 +52,7 @@ foreach my $tar_file (@ARGV) Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; last unless $name; + next if $name =~ '/$'; $mode = oct $mode; $size = oct $size; $mtime = oct $mtime; -- cgit v1.2.3 From d0c32b63394992f8dd083a4f2f380ab190dbb2ca Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 29 Apr 2007 00:31:14 -0700 Subject: Fix import-tars fix. This heeds advice from our resident Perl expert to make sure the script is not confused with a string that ends with /\n Signed-off-by: Junio C Hamano --- contrib/fast-import/import-tars.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index e84647770a..82a90429c8 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -52,7 +52,7 @@ foreach my $tar_file (@ARGV) Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; last unless $name; - next if $name =~ '/$'; + next if $name =~ m{/\z}; $mode = oct $mode; $size = oct $size; $mtime = oct $mtime; -- cgit v1.2.3 From ff5dba20e324fb394b7077d0b1d1c5eb4357959f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 1 May 2007 18:28:38 +0200 Subject: Doc cleanups. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 4f6a680f95..d36a1cf18c 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -4,8 +4,8 @@ Usage ===== git-p4 supports two main modes: Importing from Perforce to a Git repository is -done using "git-p4 sync". Submitting changes from Git back to Perforce is -done using "git-p4 submit". +done using "git-p4 sync" or "git-p4 rebase". Submitting changes from Git back +to Perforce is done using "git-p4 submit". Importing ========= @@ -49,10 +49,6 @@ a big import. This may take a while. Support for Perforce integrations is still work in progress. Don't bother trying it unless you want to hack on it :) -For convenience there's also the git-p4 clone command that works similar to -git-clone and combines the creation of the git repository with the the initial -import and the branch setup - Incremental Imports =================== -- cgit v1.2.3 From 1c094184da5c7bf572c907633279890bd15d1952 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 1 May 2007 23:15:48 +0200 Subject: Micro cleanup Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index fb13469ce2..9adc66a05d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -74,7 +74,7 @@ def extractLogMessageFromGitCommit(commit): for log in os.popen("git cat-file commit %s" % commit).readlines(): if not foundTitle: if len(log) == 1: - foundTitle = 1 + foundTitle = True continue logMessage += log -- cgit v1.2.3 From 8f8725314dbed75dc1e603646ea6a2c630025f6e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 1 May 2007 23:23:00 +0200 Subject: cleanup, renamed self.globalPrefix to self.depotPath Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 80 +++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 40 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 9adc66a05d..6b74feeb64 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -465,9 +465,9 @@ class P4Sync(Command): fnum = 0 while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] - if not path.startswith(self.globalPrefix): + if not path.startswith(self.depotPath): # if not self.silent: - # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.globalPrefix, change) + # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.depotPath, change) fnum = fnum + 1 continue @@ -491,7 +491,7 @@ class P4Sync(Command): branches = Set() for file in files: - relativePath = file["path"][len(self.globalPrefix):] + relativePath = file["path"][len(self.depotPath):] # strip off the filename relativePath = relativePath[0:relativePath.rfind("/")] @@ -577,7 +577,7 @@ class P4Sync(Command): sys.exit(1); sourceLog = sourceLog[0] - relPath = source[len(self.globalPrefix):] + relPath = source[len(self.depotPath):] # strip off the filename relPath = relPath[0:relPath.rfind("/")] @@ -737,7 +737,7 @@ class P4Sync(Command): sys.exit(1); sourceLog = sourceLog[0] - relPath = source[len(self.globalPrefix):] + relPath = source[len(self.depotPath):] # strip off the filename relPath = relPath[0:relPath.rfind("/")] @@ -749,13 +749,13 @@ class P4Sync(Command): def changeIsBranchMerge(self, sourceBranch, destinationBranch, change): sourceFiles = {} - for file in p4CmdList("files %s...@%s" % (self.globalPrefix + sourceBranch + "/", change)): + for file in p4CmdList("files %s...@%s" % (self.depotPath + sourceBranch + "/", change)): if file["action"] == "delete": continue sourceFiles[file["depotFile"]] = file destinationFiles = {} - for file in p4CmdList("files %s...@%s" % (self.globalPrefix + destinationBranch + "/", change)): + for file in p4CmdList("files %s...@%s" % (self.depotPath + destinationBranch + "/", change)): destinationFiles[file["depotFile"]] = file for fileName in sourceFiles.keys(): @@ -822,9 +822,9 @@ class P4Sync(Command): def getLabels(self): self.labels = {} - l = p4CmdList("labels %s..." % self.globalPrefix) + l = p4CmdList("labels %s..." % self.depotPath) if len(l) > 0 and not self.silent: - print "Finding files belonging to labels in %s" % self.globalPrefix + print "Finding files belonging to labels in %s" % self.depotPath for output in l: label = output["label"] @@ -839,7 +839,7 @@ class P4Sync(Command): self.labels[newestChange] = [output, revisions] def run(self, args): - self.globalPrefix = "" + self.depotPath = "" self.changeRange = "" self.initialParent = "" @@ -855,7 +855,7 @@ class P4Sync(Command): [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) if len(self.previousDepotPath) > 0 and len(p4Change) > 0: p4Change = int(p4Change) + 1 - self.globalPrefix = self.previousDepotPath + self.depotPath = self.previousDepotPath self.changeRange = "@%s,#head" % p4Change self.initialParent = self.branch self.tagLastChange = False @@ -864,49 +864,49 @@ class P4Sync(Command): self.branch = "refs/heads/" + self.branch - if len(self.globalPrefix) == 0: - self.globalPrefix = self.previousDepotPath = os.popen("git repo-config --get p4.depotpath").read() + if len(self.depotPath) == 0: + self.depotPath = self.previousDepotPath = os.popen("git repo-config --get p4.depotpath").read() - if len(self.globalPrefix) != 0: - self.globalPrefix = self.globalPrefix[:-1] + if len(self.depotPath) != 0: + self.depotPath = self.depotPath[:-1] - if len(args) == 0 and len(self.globalPrefix) != 0: + if len(args) == 0 and len(self.depotPath) != 0: if not self.silent: - print "Depot path: %s" % self.globalPrefix + print "Depot path: %s" % self.depotPath elif len(args) != 1: return False else: - if len(self.globalPrefix) != 0 and self.globalPrefix != args[0]: - print "previous import used depot path %s and now %s was specified. this doesn't work!" % (self.globalPrefix, args[0]) + if len(self.depotPath) != 0 and self.depotPath != args[0]: + print "previous import used depot path %s and now %s was specified. this doesn't work!" % (self.depotPath, args[0]) sys.exit(1) - self.globalPrefix = args[0] + self.depotPath = args[0] self.revision = "" self.users = {} self.lastChange = 0 self.initialTag = "" - if self.globalPrefix.find("@") != -1: - atIdx = self.globalPrefix.index("@") - self.changeRange = self.globalPrefix[atIdx:] + if self.depotPath.find("@") != -1: + atIdx = self.depotPath.index("@") + self.changeRange = self.depotPath[atIdx:] if self.changeRange == "@all": self.changeRange = "" elif self.changeRange.find(",") == -1: self.revision = self.changeRange self.changeRange = "" - self.globalPrefix = self.globalPrefix[0:atIdx] - elif self.globalPrefix.find("#") != -1: - hashIdx = self.globalPrefix.index("#") - self.revision = self.globalPrefix[hashIdx:] - self.globalPrefix = self.globalPrefix[0:hashIdx] + self.depotPath = self.depotPath[0:atIdx] + elif self.depotPath.find("#") != -1: + hashIdx = self.depotPath.index("#") + self.revision = self.depotPath[hashIdx:] + self.depotPath = self.depotPath[0:hashIdx] elif len(self.previousDepotPath) == 0: self.revision = "#head" - if self.globalPrefix.endswith("..."): - self.globalPrefix = self.globalPrefix[:-3] + if self.depotPath.endswith("..."): + self.depotPath = self.depotPath[:-3] - if not self.globalPrefix.endswith("/"): - self.globalPrefix += "/" + if not self.depotPath.endswith("/"): + self.depotPath += "/" self.getUserMap() self.labels = {} @@ -939,15 +939,15 @@ class P4Sync(Command): self.gitError = importProcess.childerr if len(self.revision) > 0: - print "Doing initial import of %s from revision %s" % (self.globalPrefix, self.revision) + print "Doing initial import of %s from revision %s" % (self.depotPath, self.revision) details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = "Initial import of %s from the state at revision %s" % (self.globalPrefix, self.revision) + details["desc"] = "Initial import of %s from the state at revision %s" % (self.depotPath, self.revision) details["change"] = self.revision newestRevision = 0 fileCnt = 0 - for info in p4CmdList("files %s...%s" % (self.globalPrefix, self.revision)): + for info in p4CmdList("files %s...%s" % (self.depotPath, self.revision)): change = int(info["change"]) if change > newestRevision: newestRevision = change @@ -965,7 +965,7 @@ class P4Sync(Command): details["change"] = newestRevision try: - self.commit(details, self.extractFilesFromCommit(details), self.branch, self.globalPrefix) + self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPath) except IOError: print "IO error with git fast-import. Is your git version recent enough?" print self.gitError.read() @@ -984,7 +984,7 @@ class P4Sync(Command): changes.sort() else: - output = os.popen("p4 changes %s...%s" % (self.globalPrefix, self.changeRange)).readlines() + output = os.popen("p4 changes %s...%s" % (self.depotPath, self.changeRange)).readlines() for line in output: changeNum = line.split(" ")[1] @@ -1011,7 +1011,7 @@ class P4Sync(Command): if self.detectBranches: for branch in self.branchesForCommit(files): self.knownBranches.add(branch) - branchPrefix = self.globalPrefix + branch + "/" + branchPrefix = self.depotPath + branch + "/" filesForCommit = self.extractFilesInCommitToBranch(files, branchPrefix) @@ -1040,7 +1040,7 @@ class P4Sync(Command): merged = "refs/heads/" + merged self.commit(description, files, branch, branchPrefix, parent, merged) else: - self.commit(description, files, self.branch, self.globalPrefix, self.initialParent) + self.commit(description, files, self.branch, self.depotPath, self.initialParent) self.initialParent = "" except IOError: print self.gitError.read() @@ -1059,7 +1059,7 @@ class P4Sync(Command): self.gitError.close() importProcess.wait() - os.popen("git repo-config p4.depotpath %s" % self.globalPrefix).read() + os.popen("git repo-config p4.depotpath %s" % self.depotPath).read() if len(self.initialTag) > 0: os.popen("git tag -d %s" % self.initialTag).read() -- cgit v1.2.3 From 28359251395c1647bf40adcdabbe4d85d7b085af Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 1 May 2007 23:26:19 +0200 Subject: Cleanup, removed the old tagging code Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6b74feeb64..9927fa142e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -455,7 +455,6 @@ class P4Sync(Command): self.detectBranches = False self.detectLabels = False self.changesFile = "" - self.tagLastChange = True def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -858,15 +857,11 @@ class P4Sync(Command): self.depotPath = self.previousDepotPath self.changeRange = "@%s,#head" % p4Change self.initialParent = self.branch - self.tagLastChange = False if not self.silent: print "Performing incremental import into %s git branch" % self.branch self.branch = "refs/heads/" + self.branch - if len(self.depotPath) == 0: - self.depotPath = self.previousDepotPath = os.popen("git repo-config --get p4.depotpath").read() - if len(self.depotPath) != 0: self.depotPath = self.depotPath[:-1] @@ -884,7 +879,6 @@ class P4Sync(Command): self.revision = "" self.users = {} self.lastChange = 0 - self.initialTag = "" if self.depotPath.find("@") != -1: atIdx = self.depotPath.index("@") @@ -927,7 +921,6 @@ class P4Sync(Command): self.rev = int(output[tagIdx + 9 : endPos]) + 1 self.changeRange = "@%s,#head" % self.rev self.initialParent = os.popen("git rev-parse %s" % self.branch).read()[:-1] - self.initialTag = "p4/%s" % (int(self.rev) - 1) except: pass @@ -1049,20 +1042,12 @@ class P4Sync(Command): if not self.silent: print "" - if self.tagLastChange: - self.gitStream.write("reset refs/tags/p4/%s\n" % self.lastChange) - self.gitStream.write("from %s\n\n" % self.branch); - self.gitStream.close() self.gitOutput.close() self.gitError.close() importProcess.wait() - os.popen("git repo-config p4.depotpath %s" % self.depotPath).read() - if len(self.initialTag) > 0: - os.popen("git tag -d %s" % self.initialTag).read() - return True class P4Rebase(Command): @@ -1086,7 +1071,6 @@ class P4Clone(P4Sync): self.description = "Creates a new git repository and imports from Perforce into it" self.usage = "usage: %prog [options] //depot/path[@revRange] [directory]" self.needsGit = False - self.tagLastChange = False def run(self, args): if len(args) < 1: -- cgit v1.2.3 From 775477aa1da94cb9fb9b9afdc217231a0cd42ac1 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Tue, 1 May 2007 23:42:44 +0200 Subject: Teach import-tars about GNU tar's @LongLink extension. This extension allows GNU tar to process file names in excess of the 100 characters defined by the original tar standard. It does this by faking a file, named '././@LongLink' containing the true file name, and then adding the file with a truncated name. The idea is that tar without this extension will write out a file with the long file name, and write the contents into a file with truncated name. Unfortunately, GNU tar does a lousy job at times. When truncating results in a _directory_ name, it will happily use _that_ as a truncated name for the file. An example where this actually happens is gcc-4.1.2, where the full path of the file WeThrowThisExceptionHelper.java truncates _exactly_ before the basename. So, we have to support that ad-hoc extension. This bug was noticed by Chris Riddoch on IRC. Signed-off-by: Johannes Schindelin Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 82a90429c8..e46492048c 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -52,6 +52,25 @@ foreach my $tar_file (@ARGV) Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; last unless $name; + if ($name eq '././@LongLink') { + # GNU tar extension + if (read(I, $_, 512) != 512) { + die ('Short archive'); + } + $name = unpack 'Z257', $_; + next unless $name; + + my $dummy; + if (read(I, $_, 512) != 512) { + die ('Short archive'); + } + ($dummy, $mode, $uid, $gid, $size, $mtime, + $chksum, $typeflag, $linkname, $magic, + $version, $uname, $gname, $devmajor, $devminor, + $prefix) = unpack 'Z100 Z8 Z8 Z8 Z12 Z12 + Z8 Z1 Z100 Z6 + Z2 Z32 Z32 Z8 Z8 Z*', $_; + } next if $name =~ m{/\z}; $mode = oct $mode; $size = oct $size; -- cgit v1.2.3 From a844b7406f17ac6e069e617509a2997d7e128500 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 May 2007 20:14:17 +0200 Subject: Document some implementation details, for the curious... :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index d36a1cf18c..ff0da9d0c8 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -121,3 +121,20 @@ Example git-p4 rebase +Implementation Details... +========================= + +* Changesets from Perforce are imported using git fast-import. +* The import does not require anything from the Perforce client view as it just uses + "p4 print //depot/path/file#revision" to get the actual file contents. +* Every imported changeset has a special [git-p4...] line at the + end of the log message that gives information about the corresponding + Perforce change number and is also used by git-p4 itself to find out + where to continue importing when doing incremental imports. + Basically when syncing it extracts the perforce change number of the + latest commit in the "p4" branch and uses "p4 changes //depot/path/...@changenum,#head" + to find out which changes need to be imported. +* git-p4 submit uses "git rev-list" to pick the commits between the "p4" branch + and the current branch. + The commits themselves are applied using git diff-tree ... | patch -p1 + -- cgit v1.2.3 From d966e6aa66d397908b4fbf69cef2e2da88737321 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 7 May 2007 21:13:40 -0400 Subject: Properly handle '0' filenames in import-tars Randal L. Schwartz pointed out multiple times that we should be testing the length of the name string here, not if it is "true". The problem is the string '0' is actually false in Perl when we try to evaluate it in this context, as '0' is 0 numerically and the number 0 is treated as a false value. This would cause us to break out of the import loop early if anyone had a file or directory named "0". Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index e46492048c..f0b9a43abd 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -51,7 +51,7 @@ foreach my $tar_file (@ARGV) $prefix) = unpack 'Z100 Z8 Z8 Z8 Z12 Z12 Z8 Z1 Z100 Z6 Z2 Z32 Z32 Z8 Z8 Z*', $_; - last unless $name; + last unless length($name); if ($name eq '././@LongLink') { # GNU tar extension if (read(I, $_, 512) != 512) { -- cgit v1.2.3 From 084835805565726c825f0853626a33a0263066bf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 14:31:06 +0200 Subject: Use the subprocess module instead of popen2 to make it work on Windows. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 9927fa142e..a2f582f8c9 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -8,7 +8,7 @@ # License: MIT # -import optparse, sys, os, marshal, popen2, shelve +import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time from sets import Set; @@ -926,10 +926,10 @@ class P4Sync(Command): self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) - importProcess = popen2.Popen3("git fast-import", capturestderr = True) - self.gitOutput = importProcess.fromchild - self.gitStream = importProcess.tochild - self.gitError = importProcess.childerr + importProcess = subprocess.Popen(["git", "fast-import"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE); + self.gitOutput = importProcess.stdout + self.gitStream = importProcess.stdin + self.gitError = importProcess.stderr if len(self.revision) > 0: print "Doing initial import of %s from revision %s" % (self.depotPath, self.revision) -- cgit v1.2.3 From ac1fde55a71e166a86468582961908ab0d01413b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 14:42:56 +0200 Subject: Added a little .bat wrapper from Marius Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.bat | 1 + 1 file changed, 1 insertion(+) create mode 100644 contrib/fast-import/git-p4.bat (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.bat b/contrib/fast-import/git-p4.bat new file mode 100644 index 0000000000..666eb73016 --- /dev/null +++ b/contrib/fast-import/git-p4.bat @@ -0,0 +1 @@ +python "%~d0%~p0git-p4" %* -- cgit v1.2.3 From caace111129545559bf798d83ac8cfd596d36f66 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 14:57:57 +0200 Subject: Make sure all popen calls use binary mode (for Windows) and also make gitBranchExists work on Windows. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a2f582f8c9..d83ce1ae95 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -14,6 +14,9 @@ from sets import Set; gitdir = os.environ.get("GIT_DIR", "") +def mypopen(command): + return os.popen(command, "rb"); + def p4CmdList(cmd): cmd = "p4 -G %s" % cmd pipe = os.popen(cmd, "rb") @@ -57,7 +60,7 @@ def die(msg): sys.exit(1) def currentGitBranch(): - return os.popen("git name-rev HEAD").read().split(" ")[1][:-1] + return mypopen("git name-rev HEAD").read().split(" ")[1][:-1] def isValidGitDir(path): if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): @@ -71,7 +74,7 @@ def system(cmd): def extractLogMessageFromGitCommit(commit): logMessage = "" foundTitle = False - for log in os.popen("git cat-file commit %s" % commit).readlines(): + for log in mypopen("git cat-file commit %s" % commit).readlines(): if not foundTitle: if len(log) == 1: foundTitle = True @@ -100,9 +103,8 @@ def extractDepotPathAndChangeFromGitLog(log): return values.get("depot-path"), values.get("change") def gitBranchExists(branch): - if os.system("git rev-parse %s 2>/dev/null >/dev/null" % branch) == 0: - return True - return False + proc = subprocess.Popen(["git", "rev-parse", branch], stderr=subprocess.PIPE, stdout=subprocess.PIPE); + return proc.wait() == 0; class Command: def __init__(self): @@ -146,7 +148,7 @@ class P4CleanTags(Command): caretIdx = len(output) - 1 rev = int(output[tagIdx + 9 : caretIdx]) - allTags = os.popen("git tag -l p4/").readlines() + allTags = mypopen("git tag -l p4/").readlines() for i in range(len(allTags)): allTags[i] = int(allTags[i][3:-1]) @@ -155,7 +157,7 @@ class P4CleanTags(Command): allTags.remove(rev) for rev in allTags: - print os.popen("git tag -d p4/%s" % rev).read() + print mypopen("git tag -d p4/%s" % rev).read() print "%s tags removed." % len(allTags) return True @@ -194,7 +196,7 @@ class P4Submit(Command): die("Cannot start sync. Previous sync config found at %s" % self.configFile) commits = [] - for line in os.popen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): + for line in mypopen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): commits.append(line[:-1]) commits.reverse() @@ -224,8 +226,8 @@ class P4Submit(Command): return result def apply(self, id): - print "Applying %s" % (os.popen("git log --max-count=1 --pretty=oneline %s" % id).read()) - diff = os.popen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() + print "Applying %s" % (mypopen("git log --max-count=1 --pretty=oneline %s" % id).read()) + diff = mypopen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() filesToAdd = set() filesToDelete = set() for line in diff: @@ -282,11 +284,11 @@ class P4Submit(Command): logMessage = logMessage.replace("\n", "\n\t") logMessage = logMessage[:-1] - template = os.popen("p4 change -o").read() + template = mypopen("p4 change -o").read() if self.interactive: submitTemplate = self.prepareLogMessage(template, logMessage) - diff = os.popen("p4 diff -du ...").read() + diff = mypopen("p4 diff -du ...").read() for newFile in filesToAdd: diff += "==== new file ====\n" @@ -323,7 +325,7 @@ class P4Submit(Command): print submitTemplate raw_input("Press return to continue...") else: - pipe = os.popen("p4 submit -i", "w") + pipe = mypopen("p4 submit -i", "w") pipe.write(submitTemplate) pipe.close() else: @@ -920,7 +922,7 @@ class P4Sync(Command): endPos = caretIdx self.rev = int(output[tagIdx + 9 : endPos]) + 1 self.changeRange = "@%s,#head" % self.rev - self.initialParent = os.popen("git rev-parse %s" % self.branch).read()[:-1] + self.initialParent = mypopen("git rev-parse %s" % self.branch).read()[:-1] except: pass @@ -977,7 +979,7 @@ class P4Sync(Command): changes.sort() else: - output = os.popen("p4 changes %s...%s" % (self.depotPath, self.changeRange)).readlines() + output = mypopen("p4 changes %s...%s" % (self.depotPath, self.changeRange)).readlines() for line in output: changeNum = line.split(" ")[1] @@ -1060,7 +1062,7 @@ class P4Rebase(Command): sync = P4Sync() sync.run([]) print "Rebasing the current branch" - oldHead = os.popen("git rev-parse HEAD").read()[:-1] + oldHead = mypopen("git rev-parse HEAD").read()[:-1] system("git rebase p4") system("git diff-tree --stat --summary -M %s HEAD" % oldHead) return True @@ -1176,7 +1178,7 @@ if cmd.needsGit: if len(gitdir) == 0: gitdir = ".git" if not isValidGitDir(gitdir): - cdup = os.popen("git rev-parse --show-cdup").read()[:-1] + cdup = mypopen("git rev-parse --show-cdup").read()[:-1] if isValidGitDir(cdup + "/" + gitdir): os.chdir(cdup) -- cgit v1.2.3 From 25df95cce470d74cdc5e8905a298bbc36c7ec6d0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 15:15:39 +0200 Subject: Make submitting work on Windows. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d83ce1ae95..d134a28189 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -9,7 +9,7 @@ # import optparse, sys, os, marshal, popen2, subprocess, shelve -import tempfile, getopt, sha, os.path, time +import tempfile, getopt, sha, os.path, time, platform from sets import Set; gitdir = os.environ.get("GIT_DIR", "") @@ -299,7 +299,10 @@ class P4Submit(Command): diff += "+" + line f.close() - separatorLine = "######## everything below this line is just the diff #######\n" + separatorLine = "######## everything below this line is just the diff #######" + if platform.system() == "Windows": + separatorLine += "\r" + separatorLine += "\n" response = "e" firstIteration = True @@ -312,9 +315,12 @@ class P4Submit(Command): tmpFile = os.fdopen(handle, "w+") tmpFile.write(submitTemplate + separatorLine + diff) tmpFile.close() - editor = os.environ.get("EDITOR", "vi") + defaultEditor = "vi" + if platform.system() == "Windows": + defaultEditor = "notepad" + editor = os.environ.get("EDITOR", defaultEditor); system(editor + " " + fileName) - tmpFile = open(fileName, "r") + tmpFile = open(fileName, "rb") message = tmpFile.read() tmpFile.close() os.remove(fileName) @@ -325,7 +331,7 @@ class P4Submit(Command): print submitTemplate raw_input("Press return to continue...") else: - pipe = mypopen("p4 submit -i", "w") + pipe = os.popen("p4 submit -i", "wb") pipe.write(submitTemplate) pipe.close() else: -- cgit v1.2.3 From 42890f6291ab8b718dec5a3346875c45724d4ebb Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 16:07:02 +0200 Subject: Converted to unix newlines Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.bat b/contrib/fast-import/git-p4.bat index 666eb73016..6524a54c67 100644 --- a/contrib/fast-import/git-p4.bat +++ b/contrib/fast-import/git-p4.bat @@ -1 +1 @@ -python "%~d0%~p0git-p4" %* +python "%~d0%~p0git-p4" %* -- cgit v1.2.3 From 95962f318e0f00cd4e2ebb54531c3d738aa39ece Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Tue, 15 May 2007 15:51:25 +0200 Subject: Make the command call silent Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.bat b/contrib/fast-import/git-p4.bat index 6524a54c67..9f97e884f5 100644 --- a/contrib/fast-import/git-p4.bat +++ b/contrib/fast-import/git-p4.bat @@ -1 +1 @@ -python "%~d0%~p0git-p4" %* +@python "%~d0%~p0git-p4" %* -- cgit v1.2.3 From cd6cc0d31845576082fabc7f246a99988aca6d26 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 16:15:26 +0200 Subject: Fix git-p4 clone //depot/project (head import) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d134a28189..2633f29942 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -849,6 +849,7 @@ class P4Sync(Command): self.depotPath = "" self.changeRange = "" self.initialParent = "" + self.previousDepotPath = "" if len(self.branch) == 0: self.branch = "p4" -- cgit v1.2.3 From 81f2373f89895a47ed0251ac0856798514cfb618 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 15 May 2007 23:06:43 +0200 Subject: Make git-p4 work with bare repositories. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2633f29942..84cdca1aa2 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1185,9 +1185,7 @@ if cmd.needsGit: if len(gitdir) == 0: gitdir = ".git" if not isValidGitDir(gitdir): - cdup = mypopen("git rev-parse --show-cdup").read()[:-1] - if isValidGitDir(cdup + "/" + gitdir): - os.chdir(cdup) + gitdir = mypopen("git rev-parse --git-dir").read()[:-1] if not isValidGitDir(gitdir): if isValidGitDir(gitdir + "/.git"): -- cgit v1.2.3 From d336c15835c924ba8d153edbfceca88d5c748582 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 16 May 2007 09:41:26 +0200 Subject: Added the possibility of skipping patches during git-p4 submit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 84cdca1aa2..eba7a67c68 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -230,11 +230,13 @@ class P4Submit(Command): diff = mypopen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() filesToAdd = set() filesToDelete = set() + editedFiles = set() for line in diff: modifier = line[0] path = line[1:].strip() if modifier == "M": - system("p4 edit %s" % path) + system("p4 edit \"%s\"" % path) + editedFiles.add(path) elif modifier == "A": filesToAdd.add(path) if path in filesToDelete: @@ -308,7 +310,7 @@ class P4Submit(Command): firstIteration = True while response == "e": if not firstIteration: - response = raw_input("Do you want to submit this change? [y]es/[e]dit/[n]o ") + response = raw_input("Do you want to submit this change? [y]es/[e]dit/[n]o/[s]kip ") firstIteration = False if response == "e": [handle, fileName] = tempfile.mkstemp() @@ -334,6 +336,15 @@ class P4Submit(Command): pipe = os.popen("p4 submit -i", "wb") pipe.write(submitTemplate) pipe.close() + elif response == "s": + for f in editedFiles: + system("p4 revert \"%s\"" % f); + for f in filesToAdd: + system("p4 revert \"%s\"" % f); + system("rm %s" %f) + for f in filesToDelete: + system("p4 delete \"%s\"" % f); + return else: print "Not submitting!" self.interactive = False -- cgit v1.2.3 From c3c46244518178bac49caf21d6ba3a782292bb10 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 16 May 2007 09:43:13 +0200 Subject: Give a better hint if git-p4 submit fails Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index eba7a67c68..c48b257577 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -193,7 +193,7 @@ class P4Submit(Command): def start(self): if len(self.config) > 0 and not self.reset: - die("Cannot start sync. Previous sync config found at %s" % self.configFile) + die("Cannot start sync. Previous sync config found at %s\nIf you want to start submitting again from scratch maybe you want to call git-p4 submit --reset" % self.configFile) commits = [] for line in mypopen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): -- cgit v1.2.3 From dc1a93b6dce1d38e6e0ddd8980d8ccd64937fcb1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 16 May 2007 12:12:39 +0200 Subject: Fix calling git-p4 rebase from within a subdirectory (git rebase wants to be in toplevel) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c48b257577..ca6c623809 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1197,6 +1197,8 @@ if cmd.needsGit: gitdir = ".git" if not isValidGitDir(gitdir): gitdir = mypopen("git rev-parse --git-dir").read()[:-1] + if os.path.exists(gitdir): + os.chdir(mypopen("git rev-parse --show-cdup").read()[:-1]); if not isValidGitDir(gitdir): if isValidGitDir(gitdir + "/.git"): -- cgit v1.2.3 From ca0affe7bbc0ef507e2ec01c75e4f54d309dfad7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 16 May 2007 13:15:34 +0200 Subject: A little todo note before I forget it :), based on a suggestion from Lars. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ca6c623809..70366ff8c0 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -7,6 +7,8 @@ # 2007 Trolltech ASA # License: MIT # +# TODO: Add an option to sync/rebase to fetch and rebase from origin first. +# import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time, platform -- cgit v1.2.3 From df8cfac815a1fae75afd20a86beb194d9d947971 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 16 May 2007 17:22:26 +0100 Subject: import-tars: Use the "Link indicator" to identify directories Earlier, we used the mode to determine if a name was associated with a directory. This fails, since some tar programs do not set the mode correctly. However, the link indicator _has_ to be set correctly. Noticed by Chris Riddoch. Signed-off-by: Johannes Schindelin Acked-by: Junio C Hamano Signed-off-by: Shawn O. Pearce --- contrib/fast-import/import-tars.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index f0b9a43abd..5bfd205225 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -75,7 +75,7 @@ foreach my $tar_file (@ARGV) $mode = oct $mode; $size = oct $size; $mtime = oct $mtime; - next if $mode & 0040000; + next if $typeflag == 5; # directory print FI "blob\n", "mark :$next_mark\n", "data $size\n"; while ($size > 0 && read(I, $_, 512) == 512) { -- cgit v1.2.3 From 5c4153e4880a90c05612521e9ee575a308d207db Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 07:42:38 +0200 Subject: Fixing syncing (gitdir discovery / cd) for bare repositories Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 70366ff8c0..239304857d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1200,7 +1200,9 @@ if cmd.needsGit: if not isValidGitDir(gitdir): gitdir = mypopen("git rev-parse --git-dir").read()[:-1] if os.path.exists(gitdir): - os.chdir(mypopen("git rev-parse --show-cdup").read()[:-1]); + cdup = mypopen("git rev-parse --show-cdup").read()[:-1]; + if len(cdup) > 0: + os.chdir(cdup); if not isValidGitDir(gitdir): if isValidGitDir(gitdir + "/.git"): -- cgit v1.2.3 From f9162f6a4cc9f4af3e527412b364a8eda4502920 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 09:02:45 +0200 Subject: Always pass a sha1 for the initial parent so that git-fast-import doesn't think it's creating a new branch from itself. It's a sensible error in general but in the case of incremental imports we have to apply force :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 239304857d..1f549b5c62 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -865,7 +865,7 @@ class P4Sync(Command): self.previousDepotPath = "" if len(self.branch) == 0: - self.branch = "p4" + self.branch = "refs/remotes/p4" if len(args) == 0: if not gitBranchExists(self.branch) and gitBranchExists("origin"): @@ -878,11 +878,12 @@ class P4Sync(Command): p4Change = int(p4Change) + 1 self.depotPath = self.previousDepotPath self.changeRange = "@%s,#head" % p4Change - self.initialParent = self.branch + self.initialParent = mypopen("git rev-parse %s" % self.branch).read()[:-1] if not self.silent: print "Performing incremental import into %s git branch" % self.branch - self.branch = "refs/heads/" + self.branch + if not self.branch.startswith("refs/"): + self.branch = "refs/heads/" + self.branch if len(self.depotPath) != 0: self.depotPath = self.depotPath[:-1] -- cgit v1.2.3 From 463e8af65577b1df4495cb08640840234ac80c86 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 09:13:54 +0200 Subject: Clean up code duplication for revision parsing and fix previous commit to not import into remotes/p4 (yet!). Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1f549b5c62..e18f3cb8ab 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -69,6 +69,9 @@ def isValidGitDir(path): return True; return False +def parseRevision(ref): + return mypopen("git rev-parse %s" % ref).read()[:-1] + def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) @@ -865,7 +868,7 @@ class P4Sync(Command): self.previousDepotPath = "" if len(self.branch) == 0: - self.branch = "refs/remotes/p4" + self.branch = "p4" if len(args) == 0: if not gitBranchExists(self.branch) and gitBranchExists("origin"): @@ -878,7 +881,7 @@ class P4Sync(Command): p4Change = int(p4Change) + 1 self.depotPath = self.previousDepotPath self.changeRange = "@%s,#head" % p4Change - self.initialParent = mypopen("git rev-parse %s" % self.branch).read()[:-1] + self.initialParent = parseRevision(self.branch) if not self.silent: print "Performing incremental import into %s git branch" % self.branch @@ -943,7 +946,7 @@ class P4Sync(Command): endPos = caretIdx self.rev = int(output[tagIdx + 9 : endPos]) + 1 self.changeRange = "@%s,#head" % self.rev - self.initialParent = mypopen("git rev-parse %s" % self.branch).read()[:-1] + self.initialParent = parseRevision(self.branch) except: pass -- cgit v1.2.3 From 8a2820def44f163ec8909863aefa5e8f98a236ea Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 19:44:50 +0200 Subject: Removed cleantags command. It doesn't have any meaning anymore. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 39 --------------------------------------- 1 file changed, 39 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e18f3cb8ab..910c69b534 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -129,44 +129,6 @@ class P4Debug(Command): print output return True -class P4CleanTags(Command): - def __init__(self): - Command.__init__(self) - self.options = [ -# optparse.make_option("--branch", dest="branch", default="refs/heads/master") - ] - self.description = "A tool to remove stale unused tags from incremental perforce imports." - def run(self, args): - branch = currentGitBranch() - print "Cleaning out stale p4 import tags..." - sout, sin, serr = popen2.popen3("git name-rev --tags `git rev-parse %s`" % branch) - output = sout.read() - try: - tagIdx = output.index(" tags/p4/") - except: - print "Cannot find any p4/* tag. Nothing to do." - sys.exit(0) - - try: - caretIdx = output.index("^") - except: - caretIdx = len(output) - 1 - rev = int(output[tagIdx + 9 : caretIdx]) - - allTags = mypopen("git tag -l p4/").readlines() - for i in range(len(allTags)): - allTags[i] = int(allTags[i][3:-1]) - - allTags.sort() - - allTags.remove(rev) - - for rev in allTags: - print mypopen("git tag -d p4/%s" % rev).read() - - print "%s tags removed." % len(allTags) - return True - class P4Submit(Command): def __init__(self): Command.__init__(self) @@ -1161,7 +1123,6 @@ def printUsage(commands): commands = { "debug" : P4Debug(), - "clean-tags" : P4CleanTags(), "submit" : P4Submit(), "sync" : P4Sync(), "rebase" : P4Rebase(), -- cgit v1.2.3 From 1c9d393d30797decad703b642fa87b67a5236af4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 20:15:47 +0200 Subject: Removed ancient and unused code to find the last imported revision from previous imports to use for the current import by looking at the p4 tags. The current approach of using the log message works better. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 910c69b534..a19ba47481 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -895,23 +895,6 @@ class P4Sync(Command): if self.detectLabels: self.getLabels(); - if len(self.changeRange) == 0: - try: - sout, sin, serr = popen2.popen3("git name-rev --tags `git rev-parse %s`" % self.branch) - output = sout.read() - if output.endswith("\n"): - output = output[:-1] - tagIdx = output.index(" tags/p4/") - caretIdx = output.find("^") - endPos = len(output) - if caretIdx != -1: - endPos = caretIdx - self.rev = int(output[tagIdx + 9 : endPos]) + 1 - self.changeRange = "@%s,#head" % self.rev - self.initialParent = parseRevision(self.branch) - except: - pass - self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) importProcess = subprocess.Popen(["git", "fast-import"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE); -- cgit v1.2.3 From 8ead4fda3fbaba93aae46931285e9613a058c08b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 20:26:58 +0200 Subject: Create the origin based import branch using git update-ref instead of git branch so that it's possible to have the import branch in refs/remotes. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a19ba47481..4cd486eb3a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -836,7 +836,10 @@ class P4Sync(Command): if not gitBranchExists(self.branch) and gitBranchExists("origin"): if not self.silent: print "Creating %s branch in git repository based on origin" % self.branch - system("git branch %s origin" % self.branch) + branch = self.branch + if not branch.startswith("refs"): + branch = "refs/heads/" + branch + system("git update-ref %s origin" % branch) [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) if len(self.previousDepotPath) > 0 and len(p4Change) > 0: -- cgit v1.2.3 From c6d44cb1a1a9bca58d91ef414b9dbaa393d2de3a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 20:57:05 +0200 Subject: Changed the default p4 import branch to be refs/remotes/p4/{HEAD,master} instead of refs/heads/p4. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++++++- contrib/fast-import/git-p4.txt | 6 +++--- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 4cd486eb3a..3cc6481378 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -828,9 +828,15 @@ class P4Sync(Command): self.changeRange = "" self.initialParent = "" self.previousDepotPath = "" + # importing into default remotes/p4/* layout? + defaultImport = False if len(self.branch) == 0: - self.branch = "p4" + if gitBranchExists("refs/heads/p4"): + self.branch = "p4" + else: + self.branch = "refs/remotes/p4/master" + defaultImport = True if len(args) == 0: if not gitBranchExists(self.branch) and gitBranchExists("origin"): @@ -840,6 +846,8 @@ class P4Sync(Command): if not branch.startswith("refs"): branch = "refs/heads/" + branch system("git update-ref %s origin" % branch) + if defaultImport: + system("git symbolic-ref refs/remotes/p4/HEAD %s" % branch) [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) if len(self.previousDepotPath) > 0 and len(p4Change) > 0: diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index ff0da9d0c8..32aeb0ac0b 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -20,9 +20,9 @@ or This will create an empty git repository in a subdirectory called "project" (or "myproject" with the second command), import the head revision from the -specified perforce path into a git "p4" branch, create a master branch off it -and check it out. If you want the entire history (not just the head revision) then -you can simply append a "@all" to the depot path: +specified perforce path into a git "p4" branch (remotes/p4 actually), create a +master branch off it and check it out. If you want the entire history (not just +the head revision) then you can simply append a "@all" to the depot path: git-p4 clone //depot/project/main@all myproject -- cgit v1.2.3 From 48df6fd850b2d9dc52a8a89a9e6deecd2cf1d351 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 21:18:53 +0200 Subject: Bite the bullet and automatically convert old style refs/heads/p4 repositories to the new style refs/remotes/p4 branching. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3cc6481378..cb9961a571 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -832,10 +832,12 @@ class P4Sync(Command): defaultImport = False if len(self.branch) == 0: + self.branch = "refs/remotes/p4/master" if gitBranchExists("refs/heads/p4"): - self.branch = "p4" + system("git update-ref %s refs/heads/p4" % self.branch) + system("git symbolic-ref refs/remotes/p4/HEAD refs/remotes/p4/master") + system("git branch -D p4"); else: - self.branch = "refs/remotes/p4/master" defaultImport = True if len(args) == 0: -- cgit v1.2.3 From ef48f9093ced75ca20295f42b993bd390873573d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 22:17:49 +0200 Subject: Added support for git-p4 sync/rebase --with-origin. See git-p4.txt for details :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 23 +++++++++++++++++++++-- contrib/fast-import/git-p4.txt | 20 ++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index cb9961a571..e653e8cd37 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -419,7 +419,8 @@ class P4Sync(Command): optparse.make_option("--known-branches", dest="knownBranches"), optparse.make_option("--data-cache", dest="dataCache", action="store_true"), optparse.make_option("--command-cache", dest="commandCache", action="store_true"), - optparse.make_option("--detect-labels", dest="detectLabels", action="store_true") + optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), + optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -441,6 +442,7 @@ class P4Sync(Command): self.detectBranches = False self.detectLabels = False self.changesFile = "" + self.syncWithOrigin = False def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -831,6 +833,21 @@ class P4Sync(Command): # importing into default remotes/p4/* layout? defaultImport = False + if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master"): + print "Syncing with origin first as requested by calling git fetch origin" + system("git fetch origin") + [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) + [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) + if len(originPreviousDepotPath) > 0 and len(originP4Change) > 0 and len(p4Change) > 0: + if originPreviousDepotPath == p4PreviousDepotPath: + originP4Change = int(originP4Change) + p4Change = int(p4Change) + if originP4Change > p4Change: + print "origin (%s) is newer than p4 (%s). Updating p4 branch from origin." % (originP4Change, p4Change) + system("git update-ref refs/remotes/p4/master origin"); + else: + print "Cannot sync with origin. It was imported from %s while remotes/p4 was imported from %s" % (originPreviousDepotPath, p4PreviousDepotPath) + if len(self.branch) == 0: self.branch = "refs/remotes/p4/master" if gitBranchExists("refs/heads/p4"): @@ -1037,11 +1054,13 @@ class P4Sync(Command): class P4Rebase(Command): def __init__(self): Command.__init__(self) - self.options = [ ] + self.options = [ optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true") ] self.description = "Fetches the latest revision from perforce and rebases the current work (branch) against it" + self.syncWithOrigin = False def run(self, args): sync = P4Sync() + sync.syncWithOrigin = self.syncWithOrigin sync.run([]) print "Rebasing the current branch" oldHead = mypopen("git rev-parse HEAD").read()[:-1] diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 32aeb0ac0b..ac8e6cff0b 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -63,6 +63,26 @@ It is recommended to run 'git repack -a -d -f' from time to time when using incremental imports to optimally combine the individual git packs that each incremental import creates through the use of git-fast-import. + +A useful setup may be that you have a periodically updated git repository +somewhere that contains a complete import of a Perforce project. That git +repository can be used to clone the working repository from and one would +import from Perforce directly after cloning using git-p4. If the connection to +the Perforce server is slow and the working repository hasn't been synced for a +while it may be desirable to fetch changes from the origin git repository using +the efficient git protocol. git-p4 supports this through + + git-p4 sync --with-origin + +or + + git-p4 rebase --with-origin + +In that case "git fetch origin" is called and if it turns out that the origin +branch is newer than the git "p4" import branch then the latter is updated from +the former and the direct import from Perforce is resumed, which will result in +fewer changes to be imported using the slower perforce connection. + Updating ======== -- cgit v1.2.3 From 71bd9bacec6a18c2db582e1f32b1902c82359376 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 17 May 2007 22:22:26 +0200 Subject: Removed todo item that is implemented :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 -- 1 file changed, 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e653e8cd37..a21d902ea9 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -7,8 +7,6 @@ # 2007 Trolltech ASA # License: MIT # -# TODO: Add an option to sync/rebase to fetch and rebase from origin first. -# import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time, platform -- cgit v1.2.3 From 05094f987c2f141ec9b873ad6d6303b0aca173a4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 18 May 2007 20:32:35 +0200 Subject: Fix branch setup after initial clone. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a21d902ea9..2f3615bd72 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -828,8 +828,6 @@ class P4Sync(Command): self.changeRange = "" self.initialParent = "" self.previousDepotPath = "" - # importing into default remotes/p4/* layout? - defaultImport = False if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master"): print "Syncing with origin first as requested by calling git fetch origin" @@ -850,10 +848,9 @@ class P4Sync(Command): self.branch = "refs/remotes/p4/master" if gitBranchExists("refs/heads/p4"): system("git update-ref %s refs/heads/p4" % self.branch) - system("git symbolic-ref refs/remotes/p4/HEAD refs/remotes/p4/master") system("git branch -D p4"); - else: - defaultImport = True + if not gitBranchExists("refs/remotes/p4/HEAD"): + system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch) if len(args) == 0: if not gitBranchExists(self.branch) and gitBranchExists("origin"): @@ -863,8 +860,6 @@ class P4Sync(Command): if not branch.startswith("refs"): branch = "refs/heads/" + branch system("git update-ref %s origin" % branch) - if defaultImport: - system("git symbolic-ref refs/remotes/p4/HEAD %s" % branch) [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) if len(self.previousDepotPath) > 0 and len(p4Change) > 0: -- cgit v1.2.3 From 66c6a9b559a883460b1aeed7dadec713be17588c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 18 May 2007 20:39:38 +0200 Subject: Removed unused cache variables. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ---- 1 file changed, 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2f3615bd72..e164edef55 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -415,8 +415,6 @@ class P4Sync(Command): optparse.make_option("--changesfile", dest="changesFile"), optparse.make_option("--silent", dest="silent", action="store_true"), optparse.make_option("--known-branches", dest="knownBranches"), - optparse.make_option("--data-cache", dest="dataCache", action="store_true"), - optparse.make_option("--command-cache", dest="commandCache", action="store_true"), optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true") ] @@ -430,8 +428,6 @@ class P4Sync(Command): self.usage += " //depot/path[@revRange]" - self.dataCache = False - self.commandCache = False self.silent = False self.knownBranches = Set() self.createdBranches = Set() -- cgit v1.2.3 From 4b97ffb1e462c2334b38047fbf2b7e8b24c36aed Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 18 May 2007 21:45:23 +0200 Subject: Started rewriting the branch detection, based on "p4 branches" and "p4 branch -o foo". Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 287 +++++++-------------------------------------- 1 file changed, 45 insertions(+), 242 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e164edef55..e993d3f693 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -414,9 +414,9 @@ class P4Sync(Command): optparse.make_option("--detect-branches", dest="detectBranches", action="store_true"), optparse.make_option("--changesfile", dest="changesFile"), optparse.make_option("--silent", dest="silent", action="store_true"), - optparse.make_option("--known-branches", dest="knownBranches"), optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), - optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true") + optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true"), + optparse.make_option("--verbose", dest="verbose", action="store_true") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -429,7 +429,6 @@ class P4Sync(Command): self.usage += " //depot/path[@revRange]" self.silent = False - self.knownBranches = Set() self.createdBranches = Set() self.committedChanges = Set() self.branch = "" @@ -437,6 +436,7 @@ class P4Sync(Command): self.detectLabels = False self.changesFile = "" self.syncWithOrigin = False + self.verbose = False def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -461,120 +461,25 @@ class P4Sync(Command): fnum = fnum + 1 return files - def isSubPathOf(self, first, second): - if not first.startswith(second): - return False - if first == second: - return True - return first[len(second)] == "/" - def branchesForCommit(self, files): branches = Set() for file in files: - relativePath = file["path"][len(self.depotPath):] - # strip off the filename - relativePath = relativePath[0:relativePath.rfind("/")] - - # if len(branches) == 0: - # branches.add(relativePath) - # knownBranches.add(relativePath) - # continue - - ###### this needs more testing :) - knownBranch = False - for branch in branches: - if relativePath == branch: - knownBranch = True - break - # if relativePath.startswith(branch): - if self.isSubPathOf(relativePath, branch): - knownBranch = True - break - # if branch.startswith(relativePath): - if self.isSubPathOf(branch, relativePath): - branches.remove(branch) - break - - if knownBranch: - continue - - for branch in self.knownBranches: - #if relativePath.startswith(branch): - if self.isSubPathOf(relativePath, branch): - if len(branches) == 0: - relativePath = branch - else: - knownBranch = True - break + path = file["path"][len(self.depotPath):] - if knownBranch: - continue - - branches.add(relativePath) - self.knownBranches.add(relativePath) + for branch in self.knownBranches.keys(): + if path.startswith(branch): + branches.add(branch) return branches - def findBranchParent(self, branchPrefix, files): - for file in files: - path = file["path"] - if not path.startswith(branchPrefix): - continue - action = file["action"] - if action != "integrate" and action != "branch": - continue - rev = file["rev"] - depotPath = path + "#" + rev - - log = p4CmdList("filelog \"%s\"" % depotPath) - if len(log) != 1: - print "eek! I got confused by the filelog of %s" % depotPath - sys.exit(1); - - log = log[0] - if log["action0"] != action: - print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) - sys.exit(1); - - branchAction = log["how0,0"] - # if branchAction == "branch into" or branchAction == "ignored": - # continue # ignore for branching - - if not branchAction.endswith(" from"): - continue # ignore for branching - # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) - # sys.exit(1); - - source = log["file0,0"] - if source.startswith(branchPrefix): - continue - - lastSourceRev = log["erev0,0"] - - sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) - if len(sourceLog) != 1: - print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) - sys.exit(1); - sourceLog = sourceLog[0] - - relPath = source[len(self.depotPath):] - # strip off the filename - relPath = relPath[0:relPath.rfind("/")] - - for branch in self.knownBranches: - if self.isSubPathOf(relPath, branch): - # print "determined parent branch branch %s due to change in file %s" % (branch, source) - return branch - # else: - # print "%s is not a subpath of branch %s" % (relPath, branch) - - return "" - - def commit(self, details, files, branch, branchPrefix, parent = "", merged = ""): + def commit(self, details, files, branch, branchPrefix, parent = ""): epoch = details["time"] author = details["user"] + if self.verbose: + print "commit into %s" % branch + self.gitStream.write("commit %s\n" % branch) # gitStream.write("mark :%s\n" % details["change"]) self.committedChanges.add(int(details["change"])) @@ -592,11 +497,10 @@ class P4Sync(Command): self.gitStream.write("EOT\n\n") if len(parent) > 0: + if self.verbose: + print "parent %s" % parent self.gitStream.write("from %s\n" % parent) - if len(merged) > 0: - self.gitStream.write("merge %s\n" % merged) - for file in files: path = file["path"] if not path.startswith(branchPrefix): @@ -680,118 +584,6 @@ class P4Sync(Command): return newFiles - def findBranchSourceHeuristic(self, files, branch, branchPrefix): - for file in files: - action = file["action"] - if action != "integrate" and action != "branch": - continue - path = file["path"] - rev = file["rev"] - depotPath = path + "#" + rev - - log = p4CmdList("filelog \"%s\"" % depotPath) - if len(log) != 1: - print "eek! I got confused by the filelog of %s" % depotPath - sys.exit(1); - - log = log[0] - if log["action0"] != action: - print "eek! wrong action in filelog for %s : found %s, expected %s" % (depotPath, log["action0"], action) - sys.exit(1); - - branchAction = log["how0,0"] - - if not branchAction.endswith(" from"): - continue # ignore for branching - # print "eek! file %s was not branched from but instead: %s" % (depotPath, branchAction) - # sys.exit(1); - - source = log["file0,0"] - if source.startswith(branchPrefix): - continue - - lastSourceRev = log["erev0,0"] - - sourceLog = p4CmdList("filelog -m 1 \"%s%s\"" % (source, lastSourceRev)) - if len(sourceLog) != 1: - print "eek! I got confused by the source filelog of %s%s" % (source, lastSourceRev) - sys.exit(1); - sourceLog = sourceLog[0] - - relPath = source[len(self.depotPath):] - # strip off the filename - relPath = relPath[0:relPath.rfind("/")] - - for candidate in self.knownBranches: - if self.isSubPathOf(relPath, candidate) and candidate != branch: - return candidate - - return "" - - def changeIsBranchMerge(self, sourceBranch, destinationBranch, change): - sourceFiles = {} - for file in p4CmdList("files %s...@%s" % (self.depotPath + sourceBranch + "/", change)): - if file["action"] == "delete": - continue - sourceFiles[file["depotFile"]] = file - - destinationFiles = {} - for file in p4CmdList("files %s...@%s" % (self.depotPath + destinationBranch + "/", change)): - destinationFiles[file["depotFile"]] = file - - for fileName in sourceFiles.keys(): - integrations = [] - deleted = False - integrationCount = 0 - for integration in p4CmdList("integrated \"%s\"" % fileName): - toFile = integration["fromFile"] # yes, it's true, it's fromFile - if not toFile in destinationFiles: - continue - destFile = destinationFiles[toFile] - if destFile["action"] == "delete": - # print "file %s has been deleted in %s" % (fileName, toFile) - deleted = True - break - integrationCount += 1 - if integration["how"] == "branch from": - continue - - if int(integration["change"]) == change: - integrations.append(integration) - continue - if int(integration["change"]) > change: - continue - - destRev = int(destFile["rev"]) - - startRev = integration["startFromRev"][1:] - if startRev == "none": - startRev = 0 - else: - startRev = int(startRev) - - endRev = integration["endFromRev"][1:] - if endRev == "none": - endRev = 0 - else: - endRev = int(endRev) - - initialBranch = (destRev == 1 and integration["how"] != "branch into") - inRange = (destRev >= startRev and destRev <= endRev) - newer = (destRev > startRev and destRev > endRev) - - if initialBranch or inRange or newer: - integrations.append(integration) - - if deleted: - continue - - if len(integrations) == 0 and integrationCount > 1: - print "file %s was not integrated from %s into %s" % (fileName, sourceBranch, destinationBranch) - return False - - return True - def getUserMap(self): self.users = {} @@ -819,6 +611,27 @@ class P4Sync(Command): self.labels[newestChange] = [output, revisions] + def getBranchMapping(self): + # map from branch depot path to parent branch + self.knownBranches = {} + + for info in p4CmdList("branches"): + details = p4Cmd("branch -o %s" % info["branch"]) + viewIdx = 0 + while details.has_key("View%s" % viewIdx): + paths = details["View%s" % viewIdx].split(" ") + viewIdx = viewIdx + 1 + # require standard //depot/foo/... //depot/bar/... mapping + if len(paths) != 2 or not paths[0].endswith("/...") or not paths[1].endswith("/..."): + continue + source = paths[0] + destination = paths[1] + if source.startswith(self.depotPath) and destination.startswith(self.depotPath): + source = source[len(self.depotPath):-4] + destination = destination[len(self.depotPath):-4] + self.knownBranches[destination] = source + self.knownBranches[source] = source + def run(self, args): self.depotPath = "" self.changeRange = "" @@ -914,6 +727,9 @@ class P4Sync(Command): if self.detectLabels: self.getLabels(); + if self.detectBranches: + self.getBranchMapping(); + self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) importProcess = subprocess.Popen(["git", "fast-import"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE); @@ -993,35 +809,22 @@ class P4Sync(Command): files = self.extractFilesFromCommit(description) if self.detectBranches: for branch in self.branchesForCommit(files): - self.knownBranches.add(branch) branchPrefix = self.depotPath + branch + "/" - filesForCommit = self.extractFilesInCommitToBranch(files, branchPrefix) - - merged = "" parent = "" - ########### remove cnt!!! - if branch not in self.createdBranches and cnt > 2: + + filesForCommit = self.extractFilesInCommitToBranch(files, branch) + + if branch not in self.createdBranches : self.createdBranches.add(branch) - parent = self.findBranchParent(branchPrefix, files) + parent = self.knownBranches[branch] if parent == branch: parent = "" - # elif len(parent) > 0: - # print "%s branched off of %s" % (branch, parent) - - if len(parent) == 0: - merged = self.findBranchSourceHeuristic(filesForCommit, branch, branchPrefix) - if len(merged) > 0: - print "change %s could be a merge from %s into %s" % (description["change"], merged, branch) - if not self.changeIsBranchMerge(merged, branch, int(description["change"])): - merged = "" - branch = "refs/heads/" + branch + branch = "refs/remotes/p4/" + branch if len(parent) > 0: - parent = "refs/heads/" + parent - if len(merged) > 0: - merged = "refs/heads/" + merged - self.commit(description, files, branch, branchPrefix, parent, merged) + parent = "refs/remotes/p4/" + parent + self.commit(description, files, branch, branchPrefix, parent) else: self.commit(description, files, self.branch, self.depotPath, self.initialParent) self.initialParent = "" -- cgit v1.2.3 From 8f9b2e082b54787676bdad793ca013ba4fb4e407 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 18 May 2007 22:13:26 +0200 Subject: Give branches a nice project prefix and don't bail out on clone if we failed to detect the master branch. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e993d3f693..7d42739637 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -729,6 +729,7 @@ class P4Sync(Command): if self.detectBranches: self.getBranchMapping(); + self.branchPrefix = self.depotPath[self.depotPath[:-1].rfind("/") + 1:] self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) @@ -815,12 +816,23 @@ class P4Sync(Command): filesForCommit = self.extractFilesInCommitToBranch(files, branch) - if branch not in self.createdBranches : + if branch not in self.createdBranches: self.createdBranches.add(branch) parent = self.knownBranches[branch] if parent == branch: parent = "" + # main branch? use master + if branch == "main": + branch = "master" + else: + branch = self.branchPrefix + branch + + if parent == "main": + parent = "master" + elif len(parent) > 0: + parent = self.branchPrefix + parent + branch = "refs/remotes/p4/" + branch if len(parent) > 0: parent = "refs/remotes/p4/" + parent @@ -906,8 +918,11 @@ class P4Clone(P4Sync): if not P4Sync.run(self, [depotPath]): return False if self.branch != "master": - system("git branch master p4") - system("git checkout -f") + if gitBranchExists("refs/remotes/p4/master"): + system("git branch master refs/remotes/p4/master") + system("git checkout -f") + else: + print "Could not detect main branch. No checkout/master branch created." return True class HelpFormatter(optparse.IndentedHelpFormatter): -- cgit v1.2.3 From 29bdbac1cd5fc4126b62c9a030403d56ae43c204 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 19 May 2007 10:23:12 +0200 Subject: More work on the incremental importing of multiple branches. Improved error detection by checking the exit code of git-fast-import. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 90 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 7d42739637..2c1dc9e2b3 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -612,8 +612,7 @@ class P4Sync(Command): self.labels[newestChange] = [output, revisions] def getBranchMapping(self): - # map from branch depot path to parent branch - self.knownBranches = {} + self.projectName = self.depotPath[self.depotPath[:-1].rfind("/") + 1:] for info in p4CmdList("branches"): details = p4Cmd("branch -o %s" % info["branch"]) @@ -629,16 +628,34 @@ class P4Sync(Command): if source.startswith(self.depotPath) and destination.startswith(self.depotPath): source = source[len(self.depotPath):-4] destination = destination[len(self.depotPath):-4] - self.knownBranches[destination] = source - self.knownBranches[source] = source + if destination not in self.knownBranches: + self.knownBranches[destination] = source + if source not in self.knownBranches: + self.knownBranches[source] = source + + def listExistingP4GitBranches(self): + self.p4BranchesInGit = [] + + for line in mypopen("git rev-parse --symbolic --remotes").readlines(): + if line.startswith("p4/") and line != "p4/HEAD\n": + branch = line[3:-1] + self.p4BranchesInGit.append(branch) + self.initialParents["refs/remotes/p4/" + branch] = parseRevision(line[:-1]) def run(self, args): self.depotPath = "" self.changeRange = "" self.initialParent = "" self.previousDepotPath = "" + # map from branch depot path to parent branch + self.knownBranches = {} + self.initialParents = {} + + self.listExistingP4GitBranches() + + if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master") and not self.detectBranches: + ### needs to be ported to multi branch import - if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master"): print "Syncing with origin first as requested by calling git fetch origin" system("git fetch origin") [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) @@ -662,7 +679,8 @@ class P4Sync(Command): system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch) if len(args) == 0: - if not gitBranchExists(self.branch) and gitBranchExists("origin"): + if not gitBranchExists(self.branch) and gitBranchExists("origin") and not self.detectBranches: + ### needs to be ported to multi branch import if not self.silent: print "Creating %s branch in git repository based on origin" % self.branch branch = self.branch @@ -670,11 +688,33 @@ class P4Sync(Command): branch = "refs/heads/" + branch system("git update-ref %s origin" % branch) - [self.previousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.branch)) - if len(self.previousDepotPath) > 0 and len(p4Change) > 0: - p4Change = int(p4Change) + 1 + if self.verbose: + print "branches: %s" % self.p4BranchesInGit + + p4Change = 0 + for branch in self.p4BranchesInGit: + depotPath, change = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("refs/remotes/p4/" + branch)) + + if self.verbose: + print "path %s change %s" % (depotPath, change) + + if len(depotPath) > 0 and len(change) > 0: + change = int(change) + 1 + p4Change = max(p4Change, change) + + if len(self.previousDepotPath) == 0: + self.previousDepotPath = depotPath + else: + i = 0 + l = min(len(self.previousDepotPath), len(depotPath)) + while i < l and self.previousDepotPath[i] == depotPath[i]: + i = i + 1 + self.previousDepotPath = self.previousDepotPath[:i] + + if p4Change > 0: self.depotPath = self.previousDepotPath - self.changeRange = "@%s,#head" % p4Change + #self.changeRange = "@%s,#head" % p4Change + self.changeRange = "@%s,%s" % (p4Change, p4Change + 10) self.initialParent = parseRevision(self.branch) if not self.silent: print "Performing incremental import into %s git branch" % self.branch @@ -729,7 +769,13 @@ class P4Sync(Command): if self.detectBranches: self.getBranchMapping(); - self.branchPrefix = self.depotPath[self.depotPath[:-1].rfind("/") + 1:] + if self.verbose: + print "p4-git branches: %s" % self.p4BranchesInGit + print "initial parents: %s" % self.initialParents + for b in self.p4BranchesInGit: + if b != "master": + b = b[len(self.projectName):] + self.createdBranches.add(b) self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) @@ -784,6 +830,8 @@ class P4Sync(Command): changes.sort() else: + if self.verbose: + print "Getting p4 changes for %s...%s" % (self.depotPath, self.changeRange) output = mypopen("p4 changes %s...%s" % (self.depotPath, self.changeRange)).readlines() for line in output: @@ -816,26 +864,39 @@ class P4Sync(Command): filesForCommit = self.extractFilesInCommitToBranch(files, branch) + if self.verbose: + print "branch is %s" % branch + if branch not in self.createdBranches: self.createdBranches.add(branch) parent = self.knownBranches[branch] if parent == branch: parent = "" + elif self.verbose: + print "parent determined through known branches: %s" % parent # main branch? use master if branch == "main": branch = "master" else: - branch = self.branchPrefix + branch + branch = self.projectName + branch if parent == "main": parent = "master" elif len(parent) > 0: - parent = self.branchPrefix + parent + parent = self.projectName + parent branch = "refs/remotes/p4/" + branch if len(parent) > 0: parent = "refs/remotes/p4/" + parent + + if self.verbose: + print "looking for initial parent for %s; current parent is %s" % (branch, parent) + + if len(parent) == 0 and branch in self.initialParents: + parent = self.initialParents[branch] + del self.initialParents[branch] + self.commit(description, files, branch, branchPrefix, parent) else: self.commit(description, files, self.branch, self.depotPath, self.initialParent) @@ -849,9 +910,10 @@ class P4Sync(Command): self.gitStream.close() + if importProcess.wait() != 0: + die("fast-import failed: %s" % self.gitError.read()) self.gitOutput.close() self.gitError.close() - importProcess.wait() return True -- cgit v1.2.3 From d5904674d1a52620a702b4f5efa1afe21f8a5947 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 19 May 2007 11:07:32 +0200 Subject: Cleanup/speed up the branch<> file split and removed change range limitation that I added for debugging (oops). Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2c1dc9e2b3..35c5f9c696 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -461,15 +461,17 @@ class P4Sync(Command): fnum = fnum + 1 return files - def branchesForCommit(self, files): - branches = Set() + def splitFilesIntoBranches(self, files): + branches = {} for file in files: path = file["path"][len(self.depotPath):] for branch in self.knownBranches.keys(): if path.startswith(branch): - branches.add(branch) + if branch not in branches: + branches[branch] = [] + branches[branch].append(file["path"]) return branches @@ -574,16 +576,6 @@ class P4Sync(Command): if not self.silent: print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change) - def extractFilesInCommitToBranch(self, files, branchPrefix): - newFiles = [] - - for file in files: - path = file["path"] - if path.startswith(branchPrefix): - newFiles.append(file) - - return newFiles - def getUserMap(self): self.users = {} @@ -713,8 +705,7 @@ class P4Sync(Command): if p4Change > 0: self.depotPath = self.previousDepotPath - #self.changeRange = "@%s,#head" % p4Change - self.changeRange = "@%s,%s" % (p4Change, p4Change + 10) + self.changeRange = "@%s,#head" % p4Change self.initialParent = parseRevision(self.branch) if not self.silent: print "Performing incremental import into %s git branch" % self.branch @@ -857,12 +848,13 @@ class P4Sync(Command): try: files = self.extractFilesFromCommit(description) if self.detectBranches: - for branch in self.branchesForCommit(files): + branches = self.splitFilesIntoBranches(files) + for branch in branches.keys(): branchPrefix = self.depotPath + branch + "/" parent = "" - filesForCommit = self.extractFilesInCommitToBranch(files, branch) + filesForCommit = branches[branch] if self.verbose: print "branch is %s" % branch -- cgit v1.2.3 From 71b112d4a4e070170af7b5a50647f7dac9b48e56 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 19 May 2007 11:54:11 +0200 Subject: More cleanups and speedups for labels and branches Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 35c5f9c696..49114d2c6f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -461,17 +461,32 @@ class P4Sync(Command): fnum = fnum + 1 return files - def splitFilesIntoBranches(self, files): + def splitFilesIntoBranches(self, commit): branches = {} - for file in files: - path = file["path"][len(self.depotPath):] + fnum = 0 + while commit.has_key("depotFile%s" % fnum): + path = commit["depotFile%s" % fnum] + if not path.startswith(self.depotPath): + # if not self.silent: + # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.depotPath, change) + fnum = fnum + 1 + continue + + file = {} + file["path"] = path + file["rev"] = commit["rev%s" % fnum] + file["action"] = commit["action%s" % fnum] + file["type"] = commit["type%s" % fnum] + fnum = fnum + 1 + + relPath = path[len(self.depotPath):] for branch in self.knownBranches.keys(): - if path.startswith(branch): + if relPath.startswith(branch): if branch not in branches: branches[branch] = [] - branches[branch].append(file["path"]) + branches[branch].append(file) return branches @@ -542,6 +557,8 @@ class P4Sync(Command): label = self.labels[change] labelDetails = label[0] labelRevisions = label[1] + if self.verbose: + print "Change %s is labelled %s" % (change, labelDetails) files = p4CmdList("files %s...@%s" % (branchPrefix, change)) @@ -595,13 +612,15 @@ class P4Sync(Command): label = output["label"] revisions = {} newestChange = 0 - for file in p4CmdList("files //...@%s" % label): + if self.verbose: + print "Querying files for label %s" % label + for file in p4CmdList("files %s...@%s" % (self.depotPath, label)): revisions[file["depotFile"]] = file["rev"] change = int(file["change"]) if change > newestChange: newestChange = change - self.labels[newestChange] = [output, revisions] + self.labels[int(newestChange)] = [output, revisions] def getBranchMapping(self): self.projectName = self.depotPath[self.depotPath[:-1].rfind("/") + 1:] @@ -846,9 +865,8 @@ class P4Sync(Command): cnt = cnt + 1 try: - files = self.extractFilesFromCommit(description) if self.detectBranches: - branches = self.splitFilesIntoBranches(files) + branches = self.splitFilesIntoBranches(description) for branch in branches.keys(): branchPrefix = self.depotPath + branch + "/" @@ -889,8 +907,9 @@ class P4Sync(Command): parent = self.initialParents[branch] del self.initialParents[branch] - self.commit(description, files, branch, branchPrefix, parent) + self.commit(description, filesForCommit, branch, branchPrefix, parent) else: + files = self.extractFilesFromCommit(description) self.commit(description, files, self.branch, self.depotPath, self.initialParent) self.initialParent = "" except IOError: -- cgit v1.2.3 From 9bda3a8556681c90309ec34e2ee5ae50306b61a6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 19 May 2007 12:05:40 +0200 Subject: Removed unused variable, more cleanups Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 49114d2c6f..f76d198d64 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -551,9 +551,7 @@ class P4Sync(Command): change = int(details["change"]) - self.lastChange = change - - if change in self.labels: + if self.labels.has_key(change): label = self.labels[change] labelDetails = label[0] labelRevisions = label[1] @@ -620,7 +618,10 @@ class P4Sync(Command): if change > newestChange: newestChange = change - self.labels[int(newestChange)] = [output, revisions] + self.labels[newestChange] = [output, revisions] + + if self.verbose: + print "Label changes: %s" % self.labels.keys() def getBranchMapping(self): self.projectName = self.depotPath[self.depotPath[:-1].rfind("/") + 1:] @@ -748,7 +749,6 @@ class P4Sync(Command): self.revision = "" self.users = {} - self.lastChange = 0 if self.depotPath.find("@") != -1: atIdx = self.depotPath.index("@") -- cgit v1.2.3 From b607e71efdd23cd676645b5d7bf49d985834fab8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 10:55:54 +0200 Subject: Cache the output of "p4 users" for faster syncs on high latency links. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f76d198d64..e5e7c6be12 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -501,6 +501,8 @@ class P4Sync(Command): # gitStream.write("mark :%s\n" % details["change"]) self.committedChanges.add(int(details["change"])) committer = "" + if author not in self.users: + self.getUserMapFromPerforceServer() if author in self.users: committer = "%s %s %s" % (self.users[author], epoch, self.tz) else: @@ -591,7 +593,7 @@ class P4Sync(Command): if not self.silent: print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change) - def getUserMap(self): + def getUserMapFromPerforceServer(self): self.users = {} for output in p4CmdList("users"): @@ -599,6 +601,23 @@ class P4Sync(Command): continue self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" + cache = open(gitdir + "/p4-usercache.txt", "wb") + for user in self.users.keys(): + cache.write("%s\t%s\n" % (user, self.users[user])) + cache.close(); + + def loadUserMapFromCache(self): + self.users = {} + try: + cache = open(gitdir + "/p4-usercache.txt", "rb") + lines = cache.readlines() + cache.close() + for line in lines: + entry = line[:-1].split("\t") + self.users[entry[0]] = entry[1] + except IOError: + self.getUserMapFromPerforceServer() + def getLabels(self): self.labels = {} @@ -772,7 +791,7 @@ class P4Sync(Command): if not self.depotPath.endswith("/"): self.depotPath += "/" - self.getUserMap() + self.loadUserMapFromCache() self.labels = {} if self.detectLabels: self.getLabels(); -- cgit v1.2.3 From 59fa4171090ae8dcc7087ac617dca40e66f9b33a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 15:15:34 +0200 Subject: Fix gitdir not being set when cloning. Needed for writing the p4 users cache. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e5e7c6be12..14be55bcfc 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -972,6 +972,8 @@ class P4Clone(P4Sync): self.needsGit = False def run(self, args): + global gitdir + if len(args) < 1: return False depotPath = args[0] @@ -1007,6 +1009,7 @@ class P4Clone(P4Sync): os.makedirs(dir) os.chdir(dir) system("git init") + gitdir = os.getcwd() if not P4Sync.run(self, [depotPath]): return False if self.branch != "master": -- cgit v1.2.3 From 64ffb06a9c940e57a215d313cd58f702d695e919 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 15:24:01 +0200 Subject: Oops, not only /set/ gitdir on clone, also set it /correctly/ :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 14be55bcfc..80d966fb30 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1009,7 +1009,7 @@ class P4Clone(P4Sync): os.makedirs(dir) os.chdir(dir) system("git init") - gitdir = os.getcwd() + gitdir = os.getcwd() + "/.git" if not P4Sync.run(self, [depotPath]): return False if self.branch != "master": -- cgit v1.2.3 From 47a130b7bf74b8f61d9fee01f5dbb745d8f44b29 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 16:33:21 +0200 Subject: Use git format-patch and git apply --apply when extracting patches from git and applying them to a Perforce checkout. This should make it possible to apply git commits with binary files that cannot be handled by path. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 80d966fb30..0b1df09cb8 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -213,10 +213,13 @@ class P4Submit(Command): else: die("unknown modifier %s for %s" % (modifier, path)) - diffcmd = "git diff-tree -p --diff-filter=ACMRTUXB \"%s^\" \"%s\"" % (id, id) - patchcmd = diffcmd + " | patch -p1" + diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id) + patchcmd = diffcmd + " | git apply " + tryPatchCmd = diffcmd + "--check -" + applyPatchCmd = diffcmd + "--check --apply -" + print mypopen(diffcmd).read() - if os.system(patchcmd + " --dry-run --silent") != 0: + if os.system(tryPatchCmd) != 0: print "Unfortunately applying the change failed!" print "What do you want to do?" response = "x" @@ -226,7 +229,7 @@ class P4Submit(Command): print "Skipping! Good luck with the next patches..." return elif response == "a": - os.system(patchcmd) + os.system(applyPatchCmd) if len(filesToAdd) > 0: print "You may also want to call p4 add on the following files:" print " ".join(filesToAdd) @@ -239,7 +242,7 @@ class P4Submit(Command): print "Patch saved to patch.txt in %s !" % self.clientPath die("Please resolve and submit the conflict manually and continue afterwards with git-p4 submit --continue") - system(patchcmd) + system(applyPatchCmd) for f in filesToAdd: system("p4 add %s" % f) -- cgit v1.2.3 From c1b296b9f19a62aa9e444cc454b3d62ffaa98da4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 16:55:05 +0200 Subject: Added support for git-p4 submit --direct (experimental) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 61 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 0b1df09cb8..bcea4cf3de 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -137,6 +137,7 @@ class P4Submit(Command): optparse.make_option("--log-substitutions", dest="substFile"), optparse.make_option("--noninteractive", action="store_false"), optparse.make_option("--dry-run", action="store_true"), + optparse.make_option("--direct", dest="directSubmit", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@ -147,6 +148,7 @@ class P4Submit(Command): self.substFile = "" self.firstTime = True self.origin = "" + self.directSubmit = False self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" @@ -161,9 +163,12 @@ class P4Submit(Command): die("Cannot start sync. Previous sync config found at %s\nIf you want to start submitting again from scratch maybe you want to call git-p4 submit --reset" % self.configFile) commits = [] - for line in mypopen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): - commits.append(line[:-1]) - commits.reverse() + if self.directSubmit: + commits.append("0") + else: + for line in mypopen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): + commits.append(line[:-1]) + commits.reverse() self.config["commits"] = commits @@ -191,8 +196,12 @@ class P4Submit(Command): return result def apply(self, id): - print "Applying %s" % (mypopen("git log --max-count=1 --pretty=oneline %s" % id).read()) - diff = mypopen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() + if self.directSubmit: + print "Applying local change in working directory/index" + diff = self.diffStatus + else: + print "Applying %s" % (mypopen("git log --max-count=1 --pretty=oneline %s" % id).read()) + diff = mypopen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() filesToAdd = set() filesToDelete = set() editedFiles = set() @@ -213,11 +222,13 @@ class P4Submit(Command): else: die("unknown modifier %s for %s" % (modifier, path)) - diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id) + if self.directSubmit: + diffcmd = "cat \"%s\"" % self.diffFile + else: + diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id) patchcmd = diffcmd + " | git apply " - tryPatchCmd = diffcmd + "--check -" - applyPatchCmd = diffcmd + "--check --apply -" - print mypopen(diffcmd).read() + tryPatchCmd = patchcmd + "--check -" + applyPatchCmd = patchcmd + "--check --apply -" if os.system(tryPatchCmd) != 0: print "Unfortunately applying the change failed!" @@ -250,9 +261,11 @@ class P4Submit(Command): system("p4 revert %s" % f) system("p4 delete %s" % f) - logMessage = extractLogMessageFromGitCommit(id) - logMessage = logMessage.replace("\n", "\n\t") - logMessage = logMessage[:-1] + logMessage = "" + if not self.directSubmit: + logMessage = extractLogMessageFromGitCommit(id) + logMessage = logMessage.replace("\n", "\n\t") + logMessage = logMessage[:-1] template = mypopen("p4 change -o").read() @@ -356,6 +369,15 @@ class P4Submit(Command): print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath) oldWorkingDirectory = os.getcwd() + + if self.directSubmit: + self.diffStatus = mypopen("git diff -r --name-status HEAD").readlines() + patch = mypopen("git diff -p --binary --diff-filter=ACMRTUXB HEAD").read() + self.diffFile = gitdir + "/p4-git-diff" + f = open(self.diffFile, "wb") + f.write(patch) + f.close(); + os.chdir(self.clientPath) response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % self.clientPath) if response == "y" or response == "yes": @@ -395,14 +417,25 @@ class P4Submit(Command): self.config.close() + if self.directSubmit: + os.remove(self.diffFile) + if len(commits) == 0: if self.firstTime: print "No changes found to apply between %s and current HEAD" % self.origin else: print "All changes applied!" - response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") + response = "" + os.chdir(oldWorkingDirectory) + + if self.directSubmit: + response = raw_input("Do you want to DISCARD your git WORKING DIRECTORY CHANGES and sync from Perforce now using git-p4 rebase? [y]es/[n]o ") + if response == "y" or response == "yes": + system("git reset --hard") + + if len(response) == 0: + response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") if response == "y" or response == "yes": - os.chdir(oldWorkingDirectory) rebase = P4Rebase() rebase.run([]) os.remove(self.configFile) -- cgit v1.2.3 From 8a5fc95b43ab25e7ce0ca14cf733669e5708b448 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 23:39:40 +0200 Subject: Specifying --detect-branches is now only needed for the initial clone/sync. Afterwards it's turned on implicitly if more p4 branches than remotes/p4/master are found. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bcea4cf3de..d4bf67333f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -719,6 +719,9 @@ class P4Sync(Command): self.initialParents = {} self.listExistingP4GitBranches() + if len(self.p4BranchesInGit) > 1: + print "Importing from/into multiple branches" + self.detectBranches = True if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master") and not self.detectBranches: ### needs to be ported to multi branch import -- cgit v1.2.3 From 24f7b53fdd0bcc76453fc087932ca2cdecf47f9d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 23:42:22 +0200 Subject: Had an idea for debugging, record it :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d4bf67333f..92522417cd 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -7,6 +7,10 @@ # 2007 Trolltech ASA # License: MIT # +# TODO: * implement git-p4 rollback for debugging +# to roll back all p4 remote branches to a commit older or equal to +# the specified change. +# import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time, platform -- cgit v1.2.3 From b1561ee25628c074b3afac738ead387181d3b311 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 20 May 2007 23:52:51 +0200 Subject: Another (potentially life-saving) idea for submit --direct Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 92522417cd..5055f32140 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -10,6 +10,12 @@ # TODO: * implement git-p4 rollback for debugging # to roll back all p4 remote branches to a commit older or equal to # the specified change. +# * for git-p4 submit --direct it would be nice to still create a +# git commit without updating HEAD before submitting to perforce. +# With the commit sha1 printed (or recoded in a .git/foo file?) +# it's possible to recover if anything goes wrong instead of potentially +# loosing a change entirely because it was never comitted to git and +# the p4 submit failed (or resulted in lots of conflicts, etc.) # import optparse, sys, os, marshal, popen2, subprocess, shelve -- cgit v1.2.3 From 341dc1c1793455875086373a865db4d0a132ef6e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 00:39:16 +0200 Subject: Improved output for multi branch imports and noted another little todo item Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 5055f32140..beb6529b44 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -16,6 +16,8 @@ # it's possible to recover if anything goes wrong instead of potentially # loosing a change entirely because it was never comitted to git and # the p4 submit failed (or resulted in lots of conflicts, etc.) +# * Consider making --with-origin the default, assuming that the git +# protocol is always more efficient. (needs manual testing first :) # import optparse, sys, os, marshal, popen2, subprocess, shelve @@ -729,7 +731,7 @@ class P4Sync(Command): self.initialParents = {} self.listExistingP4GitBranches() - if len(self.p4BranchesInGit) > 1: + if len(self.p4BranchesInGit) > 1 and not self.silent: print "Importing from/into multiple branches" self.detectBranches = True @@ -795,7 +797,7 @@ class P4Sync(Command): self.depotPath = self.previousDepotPath self.changeRange = "@%s,#head" % p4Change self.initialParent = parseRevision(self.branch) - if not self.silent: + if not self.silent and not self.detectBranches: print "Performing incremental import into %s git branch" % self.branch if not self.branch.startswith("refs/"): @@ -920,15 +922,17 @@ class P4Sync(Command): if len(changes) == 0: if not self.silent: - print "no changes to import!" + print "No changes to import!" return True + self.updatedBranches = set() + cnt = 1 for change in changes: description = p4Cmd("describe %s" % change) if not self.silent: - sys.stdout.write("\rimporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) sys.stdout.flush() cnt = cnt + 1 @@ -945,6 +949,8 @@ class P4Sync(Command): if self.verbose: print "branch is %s" % branch + self.updatedBranches.add(branch) + if branch not in self.createdBranches: self.createdBranches.add(branch) parent = self.knownBranches[branch] @@ -984,8 +990,13 @@ class P4Sync(Command): print self.gitError.read() sys.exit(1) - if not self.silent: - print "" + if not self.silent: + print "" + if len(self.updatedBranches) > 0: + sys.stdout.write("Updated branches: ") + for b in self.updatedBranches: + sys.stdout.write("%s " % b) + sys.stdout.write("\n") self.gitStream.close() -- cgit v1.2.3 From 56d99c67d1ad95326902f3a739ab2a3c7bb2d6fe Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Sat, 19 May 2007 23:35:21 +0200 Subject: Update bash completion to ignore some more plumbing commands [sp: Modified Jonas' original patch to keep checkout-index as a a valid completion.] Signed-off-by: Jonas Fonseca Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 46356e8a27..35b1ff9157 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -262,6 +262,7 @@ __git_commands () applypatch) : ask gittus;; archimport) : import;; cat-file) : plumbing;; + check-attr) : plumbing;; check-ref-format) : plumbing;; commit-tree) : plumbing;; convert-objects) : plumbing;; @@ -271,8 +272,10 @@ __git_commands () daemon) : daemon;; fast-import) : import;; fsck-objects) : plumbing;; + fetch--tool) : plumbing;; fetch-pack) : plumbing;; fmt-merge-msg) : plumbing;; + for-each-ref) : plumbing;; hash-object) : plumbing;; http-*) : transport;; index-pack) : plumbing;; -- cgit v1.2.3 From 33be3e6550c7051c3ac4bc624c7dacfad6974b4f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 08:44:16 +0200 Subject: Fix conversion from old style heads/p4 to remotes/p4/master Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index beb6529b44..7489c91081 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -730,11 +730,6 @@ class P4Sync(Command): self.knownBranches = {} self.initialParents = {} - self.listExistingP4GitBranches() - if len(self.p4BranchesInGit) > 1 and not self.silent: - print "Importing from/into multiple branches" - self.detectBranches = True - if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master") and not self.detectBranches: ### needs to be ported to multi branch import @@ -760,6 +755,12 @@ class P4Sync(Command): if not gitBranchExists("refs/remotes/p4/HEAD"): system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch) + # this needs to be called after the conversion from heads/p4 to remotes/p4/master + self.listExistingP4GitBranches() + if len(self.p4BranchesInGit) > 1 and not self.silent: + print "Importing from/into multiple branches" + self.detectBranches = True + if len(args) == 0: if not gitBranchExists(self.branch) and gitBranchExists("origin") and not self.detectBranches: ### needs to be ported to multi branch import -- cgit v1.2.3 From dc5240369610a6f72eab9b59447889dfd69b31c5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 09:34:56 +0200 Subject: Fix error detection with git-p4 submit when the requested depot path is not in the client view. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 7489c91081..73da5d2b27 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -55,6 +55,8 @@ def p4Where(depotPath): if not depotPath.endswith("/"): depotPath += "/" output = p4Cmd("where %s..." % depotPath) + if output["code"] == "error": + return "" clientPath = "" if "path" in output: clientPath = output.get("path") -- cgit v1.2.3 From faf1bd202640263b06c99c335269971aa9d9ead1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 10:05:30 +0200 Subject: Fix git symbolic-ref warning on initial clone Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 73da5d2b27..35a513fcb8 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -732,6 +732,8 @@ class P4Sync(Command): self.knownBranches = {} self.initialParents = {} + createP4HeadRef = False; + if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master") and not self.detectBranches: ### needs to be ported to multi branch import @@ -754,8 +756,9 @@ class P4Sync(Command): if gitBranchExists("refs/heads/p4"): system("git update-ref %s refs/heads/p4" % self.branch) system("git branch -D p4"); + # create it /after/ importing, when master exists if not gitBranchExists("refs/remotes/p4/HEAD"): - system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch) + createP4HeadRef = True # this needs to be called after the conversion from heads/p4 to remotes/p4/master self.listExistingP4GitBranches() @@ -1008,6 +1011,9 @@ class P4Sync(Command): self.gitOutput.close() self.gitError.close() + if createP4HeadRef: + system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch) + return True class P4Rebase(Command): -- cgit v1.2.3 From cbf5efa61a30d6454f5739cee3498f98a01210da Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 10:08:11 +0200 Subject: Detect with git-p4 submit --direct when there are no changes in the working directory Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 35a513fcb8..f08ee6da44 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -386,6 +386,9 @@ class P4Submit(Command): if self.directSubmit: self.diffStatus = mypopen("git diff -r --name-status HEAD").readlines() + if len(self.diffStatus) == 0: + print "No changes in working directory to submit." + return True patch = mypopen("git diff -p --binary --diff-filter=ACMRTUXB HEAD").read() self.diffFile = gitdir + "/p4-git-diff" f = open(self.diffFile, "wb") -- cgit v1.2.3 From 7944f1425c0665eef6a5b9f5cc92e15cddb42984 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 11:04:26 +0200 Subject: Make git-p4 submit --direct safer by also creating a git commit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f08ee6da44..b32d8dbfd3 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -10,12 +10,6 @@ # TODO: * implement git-p4 rollback for debugging # to roll back all p4 remote branches to a commit older or equal to # the specified change. -# * for git-p4 submit --direct it would be nice to still create a -# git commit without updating HEAD before submitting to perforce. -# With the commit sha1 printed (or recoded in a .git/foo file?) -# it's possible to recover if anything goes wrong instead of potentially -# loosing a change entirely because it was never comitted to git and -# the p4 submit failed (or resulted in lots of conflicts, etc.) # * Consider making --with-origin the default, assuming that the git # protocol is always more efficient. (needs manual testing first :) # @@ -328,9 +322,17 @@ class P4Submit(Command): print submitTemplate raw_input("Press return to continue...") else: - pipe = os.popen("p4 submit -i", "wb") - pipe.write(submitTemplate) - pipe.close() + if self.directSubmit: + print "Submitting to git first" + os.chdir(self.oldWorkingDirectory) + pipe = os.popen("git commit -a -F -", "wb") + pipe.write(submitTemplate) + pipe.close() + os.chdir(self.clientPath) + + pipe = os.popen("p4 submit -i", "wb") + pipe.write(submitTemplate) + pipe.close() elif response == "s": for f in editedFiles: system("p4 revert \"%s\"" % f); @@ -382,7 +384,7 @@ class P4Submit(Command): sys.exit(128) print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath) - oldWorkingDirectory = os.getcwd() + self.oldWorkingDirectory = os.getcwd() if self.directSubmit: self.diffStatus = mypopen("git diff -r --name-status HEAD").readlines() @@ -442,16 +444,8 @@ class P4Submit(Command): print "No changes found to apply between %s and current HEAD" % self.origin else: print "All changes applied!" - response = "" - os.chdir(oldWorkingDirectory) - - if self.directSubmit: - response = raw_input("Do you want to DISCARD your git WORKING DIRECTORY CHANGES and sync from Perforce now using git-p4 rebase? [y]es/[n]o ") - if response == "y" or response == "yes": - system("git reset --hard") - - if len(response) == 0: - response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") + os.chdir(self.oldWorkingDirectory) + response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") if response == "y" or response == "yes": rebase = P4Rebase() rebase.run([]) -- cgit v1.2.3 From 5834684d51ee6e0c21cce8d1b5df06a34a36a4f9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 22:57:06 +0200 Subject: Added a rollback command for debugging. It sets back the heads of the p4 branches to the specified p4 change number or earlier. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b32d8dbfd3..40264cdc18 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -7,10 +7,7 @@ # 2007 Trolltech ASA # License: MIT # -# TODO: * implement git-p4 rollback for debugging -# to roll back all p4 remote branches to a commit older or equal to -# the specified change. -# * Consider making --with-origin the default, assuming that the git +# TODO: * Consider making --with-origin the default, assuming that the git # protocol is always more efficient. (needs manual testing first :) # @@ -135,6 +132,35 @@ class P4Debug(Command): print output return True +class P4RollBack(Command): + def __init__(self): + Command.__init__(self) + self.options = [ + ] + self.description = "A tool to debug the multi-branch import. Don't use :)" + + def run(self, args): + if len(args) != 1: + return False + maxChange = int(args[0]) + for line in mypopen("git rev-parse --symbolic --remotes").readlines(): + if line.startswith("p4/") and line != "p4/HEAD\n": + ref = "refs/remotes/" + line[:-1] + log = extractLogMessageFromGitCommit(ref) + depotPath, change = extractDepotPathAndChangeFromGitLog(log) + changed = False + while len(change) > 0 and int(change) > maxChange: + changed = True + print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange) + system("git update-ref %s \"%s^\"" % (ref, ref)) + log = extractLogMessageFromGitCommit(ref) + depotPath, change = extractDepotPathAndChangeFromGitLog(log) + + if changed: + print "%s is at %s" % (ref, change) + + return True + class P4Submit(Command): def __init__(self): Command.__init__(self) @@ -1109,7 +1135,8 @@ commands = { "submit" : P4Submit(), "sync" : P4Sync(), "rebase" : P4Rebase(), - "clone" : P4Clone() + "clone" : P4Clone(), + "rollback" : P4RollBack() } if len(sys.argv[1:]) == 0: -- cgit v1.2.3 From af8da89cb7225c7938a836de0812467c059ca52d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 23:25:51 +0200 Subject: Fix branch detection in multi-branch imports Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 40264cdc18..515f7a906f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -556,7 +556,7 @@ class P4Sync(Command): relPath = path[len(self.depotPath):] for branch in self.knownBranches.keys(): - if relPath.startswith(branch): + if relPath.startswith(branch + "/"): # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2 if branch not in branches: branches[branch] = [] branches[branch].append(file) -- cgit v1.2.3 From 52102d4784a5f16fd84eaf98b61f714460b68693 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 21 May 2007 23:44:24 +0200 Subject: Fixes for rollback, delete branches that did not exist at the specified p4 change Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 515f7a906f..1457396abd 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -136,8 +136,10 @@ class P4RollBack(Command): def __init__(self): Command.__init__(self) self.options = [ + optparse.make_option("--verbose", dest="verbose", action="store_true") ] self.description = "A tool to debug the multi-branch import. Don't use :)" + self.verbose = False def run(self, args): if len(args) != 1: @@ -149,15 +151,22 @@ class P4RollBack(Command): log = extractLogMessageFromGitCommit(ref) depotPath, change = extractDepotPathAndChangeFromGitLog(log) changed = False + + if len(p4Cmd("changes -m 1 %s...@%s" % (depotPath, maxChange))) == 0: + print "Branch %s did not exist at change %s, deleting." % (ref, maxChange) + system("git update-ref -d %s `git rev-parse %s`" % (ref, ref)) + continue + while len(change) > 0 and int(change) > maxChange: changed = True - print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange) + if self.verbose: + print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange) system("git update-ref %s \"%s^\"" % (ref, ref)) log = extractLogMessageFromGitCommit(ref) depotPath, change = extractDepotPathAndChangeFromGitLog(log) if changed: - print "%s is at %s" % (ref, change) + print "%s rewound to %s" % (ref, change) return True -- cgit v1.2.3 From a028a98e9ae83231f0657fdb112f7d9c0cf0b98c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 00:03:08 +0200 Subject: Added support for importing multiple branches into refs/heads instead of just refs/remotes using --import-local. Needs some further microfix but seems to work otherwise. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1457396abd..969c1fe45b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -498,7 +498,8 @@ class P4Sync(Command): optparse.make_option("--silent", dest="silent", action="store_true"), optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true"), - optparse.make_option("--verbose", dest="verbose", action="store_true") + optparse.make_option("--verbose", dest="verbose", action="store_true"), + optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -519,6 +520,7 @@ class P4Sync(Command): self.changesFile = "" self.syncWithOrigin = False self.verbose = False + self.importIntoRemotes = True def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -749,11 +751,17 @@ class P4Sync(Command): def listExistingP4GitBranches(self): self.p4BranchesInGit = [] - for line in mypopen("git rev-parse --symbolic --remotes").readlines(): + cmdline = "git rev-parse --symbolic " + if self.importIntoRemotes: + cmdline += " --remotes" + else: + cmdline += " --branches" + + for line in mypopen(cmdline).readlines(): if line.startswith("p4/") and line != "p4/HEAD\n": branch = line[3:-1] self.p4BranchesInGit.append(branch) - self.initialParents["refs/remotes/p4/" + branch] = parseRevision(line[:-1]) + self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1]) def run(self, args): self.depotPath = "" @@ -764,9 +772,14 @@ class P4Sync(Command): self.knownBranches = {} self.initialParents = {} + if self.importIntoRemotes: + self.refPrefix = "refs/remotes/p4/" + else: + self.refPrefix = "refs/heads/p4/" + createP4HeadRef = False; - if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists("refs/remotes/p4/master") and not self.detectBranches: + if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists(self.refPrefix + "master") and not self.detectBranches: ### needs to be ported to multi branch import print "Syncing with origin first as requested by calling git fetch origin" @@ -779,17 +792,17 @@ class P4Sync(Command): p4Change = int(p4Change) if originP4Change > p4Change: print "origin (%s) is newer than p4 (%s). Updating p4 branch from origin." % (originP4Change, p4Change) - system("git update-ref refs/remotes/p4/master origin"); + system("git update-ref " + self.refPrefix + "master origin"); else: print "Cannot sync with origin. It was imported from %s while remotes/p4 was imported from %s" % (originPreviousDepotPath, p4PreviousDepotPath) if len(self.branch) == 0: - self.branch = "refs/remotes/p4/master" - if gitBranchExists("refs/heads/p4"): + self.branch = self.refPrefix + "master" + if gitBranchExists("refs/heads/p4") and self.importIntoRemotes: system("git update-ref %s refs/heads/p4" % self.branch) system("git branch -D p4"); # create it /after/ importing, when master exists - if not gitBranchExists("refs/remotes/p4/HEAD"): + if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: createP4HeadRef = True # this needs to be called after the conversion from heads/p4 to remotes/p4/master @@ -813,7 +826,7 @@ class P4Sync(Command): p4Change = 0 for branch in self.p4BranchesInGit: - depotPath, change = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("refs/remotes/p4/" + branch)) + depotPath, change = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.refPrefix + branch)) if self.verbose: print "path %s change %s" % (depotPath, change) @@ -1008,9 +1021,9 @@ class P4Sync(Command): elif len(parent) > 0: parent = self.projectName + parent - branch = "refs/remotes/p4/" + branch + branch = self.refPrefix + branch if len(parent) > 0: - parent = "refs/remotes/p4/" + parent + parent = self.refPrefix + parent if self.verbose: print "looking for initial parent for %s; current parent is %s" % (branch, parent) @@ -1044,7 +1057,7 @@ class P4Sync(Command): self.gitError.close() if createP4HeadRef: - system("git symbolic-ref refs/remotes/p4/HEAD %s" % self.branch) + system("git symbolic-ref %s/HEAD %s" % (self.refPrefix, self.branch)) return True -- cgit v1.2.3 From 01a9c9c5a80c6a1e82298870ca8e5bc73b2a828d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 00:07:35 +0200 Subject: Added support for --max-changes= to ease import debugging Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 969c1fe45b..3d97ce1a24 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -499,7 +499,8 @@ class P4Sync(Command): optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true"), optparse.make_option("--verbose", dest="verbose", action="store_true"), - optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false") + optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false"), + optparse.make_option("--max-changes", dest="maxChanges") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -521,6 +522,7 @@ class P4Sync(Command): self.syncWithOrigin = False self.verbose = False self.importIntoRemotes = True + self.maxChanges = "" def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -971,6 +973,9 @@ class P4Sync(Command): changes.reverse() + if len(self.maxChanges) > 0: + changes = changes[0:min(int(self.maxChanges), len(changes))] + if len(changes) == 0: if not self.silent: print "No changes to import!" -- cgit v1.2.3 From 57284050a8123a4c2d22d435a8c6daf1b53011e8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 00:15:50 +0200 Subject: Use refs/heads/* instead of refs/heads/p4/* for local imports Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3d97ce1a24..152c3c1ca5 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -760,10 +760,15 @@ class P4Sync(Command): cmdline += " --branches" for line in mypopen(cmdline).readlines(): - if line.startswith("p4/") and line != "p4/HEAD\n": + if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD\n"): + continue + if self.importIntoRemotes: + # strip off p4 branch = line[3:-1] - self.p4BranchesInGit.append(branch) - self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1]) + else: + branch = line[:-1] + self.p4BranchesInGit.append(branch) + self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1]) def run(self, args): self.depotPath = "" @@ -777,11 +782,11 @@ class P4Sync(Command): if self.importIntoRemotes: self.refPrefix = "refs/remotes/p4/" else: - self.refPrefix = "refs/heads/p4/" + self.refPrefix = "refs/heads/" createP4HeadRef = False; - if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists(self.refPrefix + "master") and not self.detectBranches: + if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists(self.refPrefix + "master") and not self.detectBranches and self.importIntoRemotes: ### needs to be ported to multi branch import print "Syncing with origin first as requested by calling git fetch origin" -- cgit v1.2.3 From a396b292678838b4cae1b358c62da6b4e0929fc2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 00:33:34 +0200 Subject: Doc updates Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index ac8e6cff0b..aa9f31e5fc 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -120,6 +120,13 @@ continue importing the remaining changes with After submitting you should sync your perforce import branch ("p4" or "origin") from Perforce using git-p4's sync command. +If you have changes in your working directory that you haven't committed into +git yet but that you want to commit to Perforce directly ("quick fixes") then +you do not have to go through the intermediate step of creating a git commit +first but you can just call + + git-p4 submit --direct + Example ======= @@ -156,5 +163,5 @@ Implementation Details... to find out which changes need to be imported. * git-p4 submit uses "git rev-list" to pick the commits between the "p4" branch and the current branch. - The commits themselves are applied using git diff-tree ... | patch -p1 + The commits themselves are applied using git diff/format-patch ... | git apply -- cgit v1.2.3 From 65d2ade95e6ef5a15a30d5115073477dfe03fb85 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 16:41:46 +0200 Subject: Avoid calling git symbolic-ref refs/heads/p4//HEAD (double slash) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 152c3c1ca5..5cea6cf9ac 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1067,7 +1067,7 @@ class P4Sync(Command): self.gitError.close() if createP4HeadRef: - system("git symbolic-ref %s/HEAD %s" % (self.refPrefix, self.branch)) + system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) return True -- cgit v1.2.3 From 0c66a783936d5cd7b1f28400c6b192c37690304a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 20:07:57 +0200 Subject: Make rollback work with locally imported branches Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 5cea6cf9ac..f12ad8baff 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -136,18 +136,28 @@ class P4RollBack(Command): def __init__(self): Command.__init__(self) self.options = [ - optparse.make_option("--verbose", dest="verbose", action="store_true") + optparse.make_option("--verbose", dest="verbose", action="store_true"), + optparse.make_option("--local", dest="rollbackLocalBranches", action="store_true") ] self.description = "A tool to debug the multi-branch import. Don't use :)" self.verbose = False + self.rollbackLocalBranches = False def run(self, args): if len(args) != 1: return False maxChange = int(args[0]) - for line in mypopen("git rev-parse --symbolic --remotes").readlines(): - if line.startswith("p4/") and line != "p4/HEAD\n": - ref = "refs/remotes/" + line[:-1] + + if self.rollbackLocalBranches: + refPrefix = "refs/heads/" + lines = mypopen("git rev-parse --symbolic --branches").readlines() + else: + refPrefix = "refs/remotes/" + lines = mypopen("git rev-parse --symbolic --remotes").readlines() + + for line in lines: + if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"): + ref = refPrefix + line[:-1] log = extractLogMessageFromGitCommit(ref) depotPath, change = extractDepotPathAndChangeFromGitLog(log) changed = False -- cgit v1.2.3 From a6d5da36af9a7087cf14e717b72c66ef2c34eb3b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 23:27:31 +0200 Subject: Don't make len(p4Cmd("p4 changes -m 1 //foo/...")) == 0 succeed when the p4 command itself failed. When the p4 command failed write out the exit code in the returned dict. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f12ad8baff..89a85ebb19 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -31,7 +31,9 @@ def p4CmdList(cmd): result.append(entry) except EOFError: pass - pipe.close() + exitCode = pipe.close() + if exitCode != None: + result["p4ExitCode"] = exitCode return result -- cgit v1.2.3 From ac3e0d79eef4535bb61d79315688fb1d225dea3b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 23:32:32 +0200 Subject: Oops, fill the /list/ correct with the p4 exit code. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 89a85ebb19..6ae3bc6e5d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -33,7 +33,9 @@ def p4CmdList(cmd): pass exitCode = pipe.close() if exitCode != None: - result["p4ExitCode"] = exitCode + entry = {} + entry["p4ExitCode"] = exitCode + result.append(entry) return result -- cgit v1.2.3 From 66a2f523958129e9b697d30ed44a5174010cb42a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 23:40:48 +0200 Subject: Catch p4 errors in rollback early enough (before deleting refs!) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6ae3bc6e5d..6d016b83d4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -152,6 +152,9 @@ class P4RollBack(Command): return False maxChange = int(args[0]) + if "p4ExitCode" in p4Cmd("p4 changes -m 1"): + die("Problems executing p4"); + if self.rollbackLocalBranches: refPrefix = "refs/heads/" lines = mypopen("git rev-parse --symbolic --branches").readlines() -- cgit v1.2.3 From ad192f2888816454df14b8a35945fb418bcb9e13 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 23:44:19 +0200 Subject: Fix p4 execution in git-p4 rollback. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6d016b83d4..b2341b7ec4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -152,7 +152,7 @@ class P4RollBack(Command): return False maxChange = int(args[0]) - if "p4ExitCode" in p4Cmd("p4 changes -m 1"): + if "p4ExitCode" in p4Cmd("changes -m 1"): die("Problems executing p4"); if self.rollbackLocalBranches: -- cgit v1.2.3 From b3fd1b28083cded86fd141c812cfd6d215ef4d5b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 23 May 2007 23:53:14 +0200 Subject: Fix multi-branch import with --silent. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b2341b7ec4..d8b7080b4b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -831,8 +831,9 @@ class P4Sync(Command): # this needs to be called after the conversion from heads/p4 to remotes/p4/master self.listExistingP4GitBranches() - if len(self.p4BranchesInGit) > 1 and not self.silent: - print "Importing from/into multiple branches" + if len(self.p4BranchesInGit) > 1: + if not self.silent: + print "Importing from/into multiple branches" self.detectBranches = True if len(args) == 0: -- cgit v1.2.3 From ebd8116870247fbc9fb27656d28f1b8a43aae766 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 May 2007 00:24:52 +0200 Subject: Load the user map from p4 only once at run-time. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d8b7080b4b..9e9d623a3c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -697,6 +697,8 @@ class P4Sync(Command): print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change) def getUserMapFromPerforceServer(self): + if self.userMapFromPerforceServer: + return self.users = {} for output in p4CmdList("users"): @@ -708,9 +710,11 @@ class P4Sync(Command): for user in self.users.keys(): cache.write("%s\t%s\n" % (user, self.users[user])) cache.close(); + self.userMapFromPerforceServer = True def loadUserMapFromCache(self): self.users = {} + self.userMapFromPerforceServer = False try: cache = open(gitdir + "/p4-usercache.txt", "rb") lines = cache.readlines() -- cgit v1.2.3 From 5cfb4fe525034b758ca39a32d05cc8b2a53d2a13 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 01:01:02 -0400 Subject: Hide the plumbing diff-{files,index,tree} from bash completion The diff-* programs are meant to be plumbing for the diff frontend; most end users aren't invoking these commands directly. Consequently we should avoid showing them as possible completions. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 35b1ff9157..9944745028 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -270,6 +270,9 @@ __git_commands () cvsimport) : import;; cvsserver) : daemon;; daemon) : daemon;; + diff-files) : plumbing;; + diff-index) : plumbing;; + diff-tree) : plumbing;; fast-import) : import;; fsck-objects) : plumbing;; fetch--tool) : plumbing;; @@ -950,7 +953,6 @@ _git () commit) _git_commit ;; config) _git_config ;; diff) _git_diff ;; - diff-tree) _git_diff_tree ;; fetch) _git_fetch ;; format-patch) _git_format_patch ;; gc) _git_gc ;; @@ -995,7 +997,6 @@ complete -o default -o nospace -F _git_cherry git-cherry complete -o default -o nospace -F _git_cherry_pick git-cherry-pick complete -o default -o nospace -F _git_commit git-commit complete -o default -o nospace -F _git_diff git-diff -complete -o default -o nospace -F _git_diff_tree git-diff-tree complete -o default -o nospace -F _git_fetch git-fetch complete -o default -o nospace -F _git_format_patch git-format-patch complete -o default -o nospace -F _git_gc git-gc @@ -1026,7 +1027,6 @@ complete -o default -o nospace -F _git git.exe complete -o default -o nospace -F _git_branch git-branch.exe complete -o default -o nospace -F _git_cherry git-cherry.exe complete -o default -o nospace -F _git_diff git-diff.exe -complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe complete -o default -o nospace -F _git_format_patch git-format-patch.exe complete -o default -o nospace -F _git_log git-log.exe complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe -- cgit v1.2.3 From 1fd6bec9bcb2d7fc710a9866bd53ea52f43d4428 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 01:25:34 -0400 Subject: Teach bash completion about git-shortlog We've had completion for git-log for quite some time, but just today I noticed we don't have it for the new builtin shortlog that runs git-log internally. This is indeed a handy thing to have completion for, especially when your branch names are of the Very-Very-Long-and-Hard/To-Type/Variety/That-Some-Use. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9944745028..d75f47a1ac 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -896,6 +896,26 @@ _git_reset () __gitcomp "$(__git_refs)" } +_git_shortlog () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --max-count= --max-age= --since= --after= + --min-age= --before= --until= + --no-merges + --author= --committer= --grep= + --all-match + --not --all + --numbered --summary + " + return + ;; + esac + __git_complete_revlist +} + _git_show () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -967,6 +987,7 @@ _git () rebase) _git_rebase ;; remote) _git_remote ;; reset) _git_reset ;; + shortlog) _git_shortlog ;; show) _git_show ;; show-branch) _git_log ;; whatchanged) _git_log ;; @@ -1012,6 +1033,7 @@ complete -o default -o nospace -F _git_rebase git-rebase complete -o default -o nospace -F _git_config git-config complete -o default -o nospace -F _git_remote git-remote complete -o default -o nospace -F _git_reset git-reset +complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged @@ -1034,6 +1056,7 @@ complete -o default -o nospace -F _git_merge_base git-merge-base.exe complete -o default -o nospace -F _git_name_rev git-name-rev.exe complete -o default -o nospace -F _git_push git-push.exe complete -o default -o nospace -F _git_config git-config +complete -o default -o nospace -F _git_shortlog git-shortlog.exe complete -o default -o nospace -F _git_show git-show.exe complete -o default -o nospace -F _git_log git-show-branch.exe complete -o default -o nospace -F _git_log git-whatchanged.exe -- cgit v1.2.3 From bfbd131f52ff8373ffabbb30f275b4af8213f5f0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 01:26:58 -0400 Subject: Remove a duplicate --not option in bash completion This was just me being silly; I put the --not option into the completion list twice. There's no duplicates shown in the shell as the shell removes them before showing them to the user. But we really don't need the duplicates in the source script either. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d75f47a1ac..24b18183fd 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -579,7 +579,7 @@ _git_log () __gitcomp " --max-count= --max-age= --since= --after= --min-age= --before= --until= - --root --not --topo-order --date-order + --root --topo-order --date-order --no-merges --abbrev-commit --abbrev= --relative-date -- cgit v1.2.3 From c70680ce7cec72a465468c223d43c08f5254d31f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 01:36:46 -0400 Subject: Update bash completion header documentation 1) Added a note about supporting the long options for most commands, as we have been doing so for quite some time. 2) Include a notice that these routines are covered by the GPL, as that may not be obvious, even though they are distributed as part of the core Git distribution. 3) Added a short section on how to send patches to the routines, and to whom they should get sent to. Currently that is me, as I am the active maintainer. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 24b18183fd..48ba3f8e81 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1,8 +1,9 @@ # # bash completion support for core Git. # -# Copyright (C) 2006,2007 Shawn Pearce +# Copyright (C) 2006,2007 Shawn O. Pearce # Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/). +# Distributed under the GNU General Public License, version 2.0. # # The contained completion routines provide support for completing: # @@ -11,6 +12,7 @@ # *) .git/remotes file names # *) git 'subcommands' # *) tree paths within 'ref:path/to/file' expressions +# *) common --long-options # # To use these routines: # @@ -31,6 +33,17 @@ # are currently in a git repository. The %s token will be # the name of the current branch. # +# To submit patches: +# +# *) Read Documentation/SubmittingPatches +# *) Send all patches to the current maintainer: +# +# "Shawn O. Pearce" +# +# *) Always CC the Git mailing list: +# +# git@vger.kernel.org +# __gitdir () { -- cgit v1.2.3 From fb72759b7de504f077250fd5bd557e3b9e2a5682 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 01:46:49 -0400 Subject: Teach bash completion about 'git remote update' Recently the git-remote command grew an update subcommand, which can be used to execute git-fetch across multiple repositories in a single step. These can be configured with the 'remotes.*' configuration options, so we can offer completion for any name that matches and appears to be useful to git-remote update. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 48ba3f8e81..d6252068c4 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -877,13 +877,13 @@ _git_remote () while [ $c -lt $COMP_CWORD ]; do i="${COMP_WORDS[c]}" case "$i" in - add|show|prune) command="$i"; break ;; + add|show|prune|update) command="$i"; break ;; esac c=$((++c)) done if [ $c -eq $COMP_CWORD -a -z "$command" ]; then - __gitcomp "add show prune" + __gitcomp "add show prune update" return fi @@ -891,6 +891,18 @@ _git_remote () show|prune) __gitcomp "$(__git_remotes)" ;; + update) + local i c='' IFS=$'\n' + for i in $(git --git-dir="$(__gitdir)" config --list); do + case "$i" in + remotes.*) + i="${i#remotes.}" + c="$c ${i/=*/}" + ;; + esac + done + __gitcomp "$c" + ;; *) COMPREPLY=() ;; -- cgit v1.2.3 From 8f87fae6459b7d5b8058691911f1c18c59a5dcbd Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 01:51:30 -0400 Subject: Teach bash completion about recent log long options (Somewhat) recently git-log learned about --reverse (to show commits in the opposite order) and a looong time ago I think it learned about --raw (to show the raw diff, rather than a unified diff). These are both useful options, so we should make them easy for the user to complete. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d6252068c4..0b8cb5f0e6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -592,13 +592,13 @@ _git_log () __gitcomp " --max-count= --max-age= --since= --after= --min-age= --before= --until= - --root --topo-order --date-order + --root --topo-order --date-order --reverse --no-merges --abbrev-commit --abbrev= --relative-date --author= --committer= --grep= --all-match - --pretty= --name-status --name-only + --pretty= --name-status --name-only --raw --not --all " return -- cgit v1.2.3 From 12977705b3da7eb63cdfd890f838b971e8d81485 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 24 May 2007 02:07:45 -0400 Subject: Update bash completion for git-config options A few new configuration options grew out of the woodwork during the 1.5.2 series. Most of these are pretty easy to support a completion of, so we do so. I wanted to also add completion support for the part of merge..name but to do that we have to look at all of the .gitattributes files and guess what the unique set of strings would be. Since this appears to be non-trivial I'm punting on it at this time. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0b8cb5f0e6..9e72f0f7b1 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -764,9 +764,11 @@ _git_config () case "$cur" in --*) __gitcomp " - --global --list --replace-all + --global --system + --list --replace-all --get --get-all --get-regexp --add --unset --unset-all + --remove-section --rename-section " return ;; @@ -785,7 +787,10 @@ _git_config () remote.*.*) local pfx="${cur%.*}." cur="${cur##*.}" - __gitcomp "url fetch push" "$pfx" "$cur" + __gitcomp " + url fetch push skipDefaultUpdate + receivepack uploadpack tagopt + " "$pfx" "$cur" return ;; remote.*) @@ -835,6 +840,9 @@ _git_config () format.headers gitcvs.enabled gitcvs.logfile + gitcvs.allbinary + gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dvpass + gc.packrefs gc.reflogexpire gc.reflogexpireunreachable gc.rerereresolved @@ -851,9 +859,11 @@ _git_config () i18n.commitEncoding i18n.logOutputEncoding log.showroot + merge.tool merge.summary merge.verbosity pack.window + pack.depth pull.octopus pull.twohead repack.useDeltaBaseOffset -- cgit v1.2.3 From c1f9197f372a5378c3dbb36adeedf37ae175ecf1 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Thu, 24 May 2007 14:07:55 +0200 Subject: Replace \r\n with \n when importing from p4 on Windows Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 9e9d623a3c..09b3cb5573 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -540,6 +540,7 @@ class P4Sync(Command): self.verbose = False self.importIntoRemotes = True self.maxChanges = "" + self.isWindows = (platform.system() == "Windows") def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -647,6 +648,9 @@ class P4Sync(Command): data = self.p4File(depotPath) + if self.isWindows and file["type"].endswith("text"): + data = data.replace("\r\n", "\n") + self.gitStream.write("M %s inline %s\n" % (mode, relPath)) self.gitStream.write("data %s\n" % len(data)) self.gitStream.write(data) -- cgit v1.2.3 From d1874ed33bc346dca8b86891757703c908634aad Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 May 2007 21:23:04 +0200 Subject: Fix creating the remotes/p4 branches based on origin/* for the multi-branch import Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 09b3cb5573..b587e79b48 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -795,6 +795,20 @@ class P4Sync(Command): self.p4BranchesInGit.append(branch) self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1]) + def createBranchesFromOrigin(self): + if not self.silent: + print "Creating branch(es) in %s based on origin branch(es)" % self.refPrefix + + for line in mypopen("git rev-parse --symbolic --remotes"): + if (not line.startswith("origin/")) or line.endswith("HEAD\n"): + continue + headName = line[len("origin/"):-1] + remoteHead = self.refPrefix + headName + if not os.path.exists(gitdir + "/" + remoteHead): + if self.verbose: + print "creating %s" % remoteHead + system("git update-ref %s origin/%s" % (remoteHead, headName)) + def run(self, args): self.depotPath = "" self.changeRange = "" @@ -841,18 +855,14 @@ class P4Sync(Command): self.listExistingP4GitBranches() if len(self.p4BranchesInGit) > 1: if not self.silent: - print "Importing from/into multiple branches" + print "Importing from/into multiple branches" self.detectBranches = True if len(args) == 0: - if not gitBranchExists(self.branch) and gitBranchExists("origin") and not self.detectBranches: - ### needs to be ported to multi branch import - if not self.silent: - print "Creating %s branch in git repository based on origin" % self.branch - branch = self.branch - if not branch.startswith("refs"): - branch = "refs/heads/" + branch - system("git update-ref %s origin" % branch) + if len(self.p4BranchesInGit) == 0: + self.createBranchesFromOrigin() + self.listExistingP4GitBranches() + return True if self.verbose: print "branches: %s" % self.p4BranchesInGit -- cgit v1.2.3 From 2cc58fd99ae5bc5845792393f232853da1ff6082 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 May 2007 22:10:40 +0200 Subject: Forgot to remove this return statement from debugging Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 1 - 1 file changed, 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b587e79b48..bfd950d53d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -862,7 +862,6 @@ class P4Sync(Command): if len(self.p4BranchesInGit) == 0: self.createBranchesFromOrigin() self.listExistingP4GitBranches() - return True if self.verbose: print "branches: %s" % self.p4BranchesInGit -- cgit v1.2.3 From abcd790fe9bc168041dcc2a0e678d22a135d33c8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 May 2007 22:25:36 +0200 Subject: Added support for --with-origin with multi-branch imports Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 58 ++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 30 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bfd950d53d..0597daa849 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -795,19 +795,37 @@ class P4Sync(Command): self.p4BranchesInGit.append(branch) self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1]) - def createBranchesFromOrigin(self): + def createOrUpdateBranchesFromOrigin(self): if not self.silent: - print "Creating branch(es) in %s based on origin branch(es)" % self.refPrefix + print "Creating/updating branch(es) in %s based on origin branch(es)" % self.refPrefix for line in mypopen("git rev-parse --symbolic --remotes"): if (not line.startswith("origin/")) or line.endswith("HEAD\n"): continue headName = line[len("origin/"):-1] remoteHead = self.refPrefix + headName + originHead = "origin/" + headName + + update = False if not os.path.exists(gitdir + "/" + remoteHead): if self.verbose: print "creating %s" % remoteHead - system("git update-ref %s origin/%s" % (remoteHead, headName)) + update = True + else: + [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead)) + [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(remoteHead)) + if len(originPreviousDepotPath) > 0 and len(originP4Change) > 0 and len(p4Change) > 0: + if originPreviousDepotPath == p4PreviousDepotPath: + originP4Change = int(originP4Change) + p4Change = int(p4Change) + if originP4Change > p4Change: + print "%s (%s) is newer than %s (%s). Updating p4 branch from origin." % (originHead, originP4Change, remoteHead, p4Change) + update = True + else: + print "Ignoring: %s was imported from %s while %s was imported from %s" % (originHead, originPreviousDepotPath, remoteHead, p4PreviousDepotPath) + + if update: + system("git update-ref %s %s" % (remoteHead, originHead)) def run(self, args): self.depotPath = "" @@ -825,23 +843,6 @@ class P4Sync(Command): createP4HeadRef = False; - if self.syncWithOrigin and gitBranchExists("origin") and gitBranchExists(self.refPrefix + "master") and not self.detectBranches and self.importIntoRemotes: - ### needs to be ported to multi branch import - - print "Syncing with origin first as requested by calling git fetch origin" - system("git fetch origin") - [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) - [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) - if len(originPreviousDepotPath) > 0 and len(originP4Change) > 0 and len(p4Change) > 0: - if originPreviousDepotPath == p4PreviousDepotPath: - originP4Change = int(originP4Change) - p4Change = int(p4Change) - if originP4Change > p4Change: - print "origin (%s) is newer than p4 (%s). Updating p4 branch from origin." % (originP4Change, p4Change) - system("git update-ref " + self.refPrefix + "master origin"); - else: - print "Cannot sync with origin. It was imported from %s while remotes/p4 was imported from %s" % (originPreviousDepotPath, p4PreviousDepotPath) - if len(self.branch) == 0: self.branch = self.refPrefix + "master" if gitBranchExists("refs/heads/p4") and self.importIntoRemotes: @@ -851,17 +852,14 @@ class P4Sync(Command): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: createP4HeadRef = True - # this needs to be called after the conversion from heads/p4 to remotes/p4/master - self.listExistingP4GitBranches() - if len(self.p4BranchesInGit) > 1: - if not self.silent: - print "Importing from/into multiple branches" - self.detectBranches = True - if len(args) == 0: - if len(self.p4BranchesInGit) == 0: - self.createBranchesFromOrigin() - self.listExistingP4GitBranches() + self.createOrUpdateBranchesFromOrigin() + self.listExistingP4GitBranches() + + if len(self.p4BranchesInGit) > 1: + if not self.silent: + print "Importing from/into multiple branches" + self.detectBranches = True if self.verbose: print "branches: %s" % self.p4BranchesInGit -- cgit v1.2.3 From 10f880f8d4e2c6390928a0bcc8b43161b5f845b2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 24 May 2007 22:28:28 +0200 Subject: Oops, fix --with-origin to /really/ also call git fetch :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 0597daa849..08af23f9fd 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -841,6 +841,10 @@ class P4Sync(Command): else: self.refPrefix = "refs/heads/" + if self.syncWithOrigin: + print "Syncing with origin first as requested by calling git fetch origin" + system("git fetch origin") + createP4HeadRef = False; if len(self.branch) == 0: -- cgit v1.2.3 From 65c5f3e3f2dfb470c16628032235222a492e3239 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 08:44:41 +0200 Subject: Avoid creating non-p4 branches in remotes/p4 off of remotes/origin Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 08af23f9fd..1cce38a6f7 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -802,19 +802,23 @@ class P4Sync(Command): for line in mypopen("git rev-parse --symbolic --remotes"): if (not line.startswith("origin/")) or line.endswith("HEAD\n"): continue + headName = line[len("origin/"):-1] remoteHead = self.refPrefix + headName originHead = "origin/" + headName + [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead)) + if len(originPreviousDepotPath) == 0 or len(originP4Change) == 0: + continue + update = False if not os.path.exists(gitdir + "/" + remoteHead): if self.verbose: print "creating %s" % remoteHead update = True else: - [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead)) [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(remoteHead)) - if len(originPreviousDepotPath) > 0 and len(originP4Change) > 0 and len(p4Change) > 0: + if len(p4Change) > 0: if originPreviousDepotPath == p4PreviousDepotPath: originP4Change = int(originP4Change) p4Change = int(p4Change) -- cgit v1.2.3 From 4280e5333354c6dddcd994bdacd3c6a11ac2da5e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 08:49:18 +0200 Subject: Make git-p4 work with packed refs (don't use os.path.exists to check for the existance of a ref) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1cce38a6f7..e8a5c1fa31 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -412,7 +412,7 @@ class P4Submit(Command): if len(args) == 0: self.master = currentGitBranch() - if len(self.master) == 0 or not os.path.exists("%s/refs/heads/%s" % (gitdir, self.master)): + if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master): die("Detecting current git branch failed!") elif len(args) == 1: self.master = args[0] @@ -812,7 +812,7 @@ class P4Sync(Command): continue update = False - if not os.path.exists(gitdir + "/" + remoteHead): + if not gitBranchExists(remoteHead): if self.verbose: print "creating %s" % remoteHead update = True -- cgit v1.2.3 From 417a7a6fc8c3d77e3e9a3bb0e11cb2eea978e20b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 10:28:46 +0200 Subject: Make --with-origin also work without origin :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e8a5c1fa31..30ee68c609 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -846,8 +846,9 @@ class P4Sync(Command): self.refPrefix = "refs/heads/" if self.syncWithOrigin: - print "Syncing with origin first as requested by calling git fetch origin" - system("git fetch origin") + if gitBranchExists("origin"): + print "Syncing with origin first as requested by calling git fetch origin" + system("git fetch origin") createP4HeadRef = False; -- cgit v1.2.3 From 01265103fe3966abd720ebb0bba1882a5701f327 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 10:36:10 +0200 Subject: Make --with-origin the default for syncing. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 16 ++++++++++------ contrib/fast-import/git-p4.txt | 14 +++----------- 2 files changed, 13 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 30ee68c609..ed5a5c593c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -118,6 +118,9 @@ def gitBranchExists(branch): proc = subprocess.Popen(["git", "rev-parse", branch], stderr=subprocess.PIPE, stdout=subprocess.PIPE); return proc.wait() == 0; +def gitConfig(key): + return mypopen("git config %s" % key).read()[:-1] + class Command: def __init__(self): self.usage = "usage: %prog [options]" @@ -514,7 +517,6 @@ class P4Sync(Command): optparse.make_option("--changesfile", dest="changesFile"), optparse.make_option("--silent", dest="silent", action="store_true"), optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), - optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true"), optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false"), optparse.make_option("--max-changes", dest="maxChanges") @@ -536,12 +538,15 @@ class P4Sync(Command): self.detectBranches = False self.detectLabels = False self.changesFile = "" - self.syncWithOrigin = False + self.syncWithOrigin = True self.verbose = False self.importIntoRemotes = True self.maxChanges = "" self.isWindows = (platform.system() == "Windows") + if gitConfig("git-p4.syncFromOrigin") == "false": + self.syncWithOrigin = False + def p4File(self, depotPath): return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() @@ -847,7 +852,8 @@ class P4Sync(Command): if self.syncWithOrigin: if gitBranchExists("origin"): - print "Syncing with origin first as requested by calling git fetch origin" + if not self.silent: + print "Syncing with origin first by calling git fetch origin" system("git fetch origin") createP4HeadRef = False; @@ -1116,13 +1122,11 @@ class P4Sync(Command): class P4Rebase(Command): def __init__(self): Command.__init__(self) - self.options = [ optparse.make_option("--with-origin", dest="syncWithOrigin", action="store_true") ] + self.options = [ ] self.description = "Fetches the latest revision from perforce and rebases the current work (branch) against it" - self.syncWithOrigin = False def run(self, args): sync = P4Sync() - sync.syncWithOrigin = self.syncWithOrigin sync.run([]) print "Rebasing the current branch" oldHead = mypopen("git rev-parse HEAD").read()[:-1] diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index aa9f31e5fc..c315158d8d 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -70,18 +70,10 @@ repository can be used to clone the working repository from and one would import from Perforce directly after cloning using git-p4. If the connection to the Perforce server is slow and the working repository hasn't been synced for a while it may be desirable to fetch changes from the origin git repository using -the efficient git protocol. git-p4 supports this through +the efficient git protocol. git-p4 supports this setup by calling "git fetch origin" +by default if there is an origin branch. You can disable this using - git-p4 sync --with-origin - -or - - git-p4 rebase --with-origin - -In that case "git fetch origin" is called and if it turns out that the origin -branch is newer than the git "p4" import branch then the latter is updated from -the former and the direct import from Perforce is resumed, which will result in -fewer changes to be imported using the slower perforce connection. + git config git-p4.syncFromOrigin false Updating ======== -- cgit v1.2.3 From d414c74afd8610c8c22dadf62b5ba8c6efa59e75 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 11:36:42 +0200 Subject: Shortcut the case where we have no origin branch Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ed5a5c593c..d99237c632 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -844,6 +844,7 @@ class P4Sync(Command): # map from branch depot path to parent branch self.knownBranches = {} self.initialParents = {} + self.hasOrigin = gitBranchExists("origin") if self.importIntoRemotes: self.refPrefix = "refs/remotes/p4/" @@ -851,7 +852,7 @@ class P4Sync(Command): self.refPrefix = "refs/heads/" if self.syncWithOrigin: - if gitBranchExists("origin"): + if self.hasOrigin: if not self.silent: print "Syncing with origin first by calling git fetch origin" system("git fetch origin") @@ -868,7 +869,8 @@ class P4Sync(Command): createP4HeadRef = True if len(args) == 0: - self.createOrUpdateBranchesFromOrigin() + if self.hasOrigin: + self.createOrUpdateBranchesFromOrigin() self.listExistingP4GitBranches() if len(self.p4BranchesInGit) > 1: -- cgit v1.2.3 From 877db584aae9816671147da45c30c31748ef287f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 19:43:38 +0200 Subject: Forgot to remove this TODO item when I made --with-origin the default :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 --- 1 file changed, 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d99237c632..0946965043 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -7,9 +7,6 @@ # 2007 Trolltech ASA # License: MIT # -# TODO: * Consider making --with-origin the default, assuming that the git -# protocol is always more efficient. (needs manual testing first :) -# import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time, platform -- cgit v1.2.3 From cb4f1280dd5691890abea4521bff0a3d6e3facfd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 25 May 2007 22:34:30 +0200 Subject: Added git-p4 submit --trust-me-like-a-fool for the adventurous users :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 0946965043..dbd5b3bcf1 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -195,9 +195,9 @@ class P4Submit(Command): optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), optparse.make_option("--log-substitutions", dest="substFile"), - optparse.make_option("--noninteractive", action="store_false"), optparse.make_option("--dry-run", action="store_true"), optparse.make_option("--direct", dest="directSubmit", action="store_true"), + optparse.make_option("--trust-me-like-a-fool", dest="trustMeLikeAFool", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@ -209,6 +209,7 @@ class P4Submit(Command): self.firstTime = True self.origin = "" self.directSubmit = False + self.trustMeLikeAFool = False self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" @@ -348,6 +349,9 @@ class P4Submit(Command): separatorLine += "\n" response = "e" + if self.trustMeLikeAFool: + response = "y" + firstIteration = True while response == "e": if not firstIteration: -- cgit v1.2.3 From a3c55c09ecae2a9c852c8c10e668c901639e845b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 27 May 2007 15:48:01 +0200 Subject: Fix creation of refs/remotes/p4/HEAD symbolic ref Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index dbd5b3bcf1..aeefadcd66 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -858,8 +858,6 @@ class P4Sync(Command): print "Syncing with origin first by calling git fetch origin" system("git fetch origin") - createP4HeadRef = False; - if len(self.branch) == 0: self.branch = self.refPrefix + "master" if gitBranchExists("refs/heads/p4") and self.importIntoRemotes: @@ -867,7 +865,7 @@ class P4Sync(Command): system("git branch -D p4"); # create it /after/ importing, when master exists if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: - createP4HeadRef = True + system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) if len(args) == 0: if self.hasOrigin: @@ -1117,9 +1115,6 @@ class P4Sync(Command): self.gitOutput.close() self.gitError.close() - if createP4HeadRef: - system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) - return True class P4Rebase(Command): -- cgit v1.2.3 From ce6f33c83537a04a3fb7557d7a74fe81ebccd7e4 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 16:46:29 -0300 Subject: Cleanups - don't use dir (python builtin) - use re for munging depotPath into destination Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index aeefadcd66..910e4ff3cd 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -10,6 +10,7 @@ import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time, platform +import re from sets import Set; gitdir = os.environ.get("GIT_DIR", "") @@ -842,6 +843,7 @@ class P4Sync(Command): self.changeRange = "" self.initialParent = "" self.previousDepotPath = "" + # map from branch depot path to parent branch self.knownBranches = {} self.initialParents = {} @@ -1145,37 +1147,26 @@ class P4Clone(P4Sync): if len(args) < 1: return False depotPath = args[0] - dir = "" + destination = "" if len(args) == 2: - dir = args[1] + destination = args[1] elif len(args) > 2: return False if not depotPath.startswith("//"): return False - if len(dir) == 0: - dir = depotPath - atPos = dir.rfind("@") - if atPos != -1: - dir = dir[0:atPos] - hashPos = dir.rfind("#") - if hashPos != -1: - dir = dir[0:hashPos] - - if dir.endswith("..."): - dir = dir[:-3] - - if dir.endswith("/"): - dir = dir[:-1] + depotDir = re.sub("(@[^@]*)$", "", depotPath) + depotDir = re.sub("(#[^#]*)$", "", depotDir) + depotDir = re.sub(r"\.\.\.$,", "", depotDir) + depotDir = re.sub(r"/$", "", depotDir) - slashPos = dir.rfind("/") - if slashPos != -1: - dir = dir[slashPos + 1:] + if not destination: + destination = os.path.split(depotDir)[-1] - print "Importing from %s into %s" % (depotPath, dir) - os.makedirs(dir) - os.chdir(dir) + print "Importing from %s into %s" % (depotPath, destination) + os.makedirs(destination) + os.chdir(destination) system("git init") gitdir = os.getcwd() + "/.git" if not P4Sync.run(self, [depotPath]): -- cgit v1.2.3 From cebdf5af319ce638862fe2e2cd2797840962ddbb Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 16:53:11 -0300 Subject: reformatting: break long lines. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 48 +++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 910e4ff3cd..aba4752d4e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -222,7 +222,9 @@ class P4Submit(Command): def start(self): if len(self.config) > 0 and not self.reset: - die("Cannot start sync. Previous sync config found at %s\nIf you want to start submitting again from scratch maybe you want to call git-p4 submit --reset" % self.configFile) + die("Cannot start sync. Previous sync config found at %s\n" + "If you want to start submitting again from scratch " + "maybe you want to call git-p4 submit --reset" % self.configFile) commits = [] if self.directSubmit: @@ -297,7 +299,8 @@ class P4Submit(Command): print "What do you want to do?" response = "x" while response != "s" and response != "a" and response != "w": - response = raw_input("[s]kip this patch / [a]pply the patch forcibly and with .rej files / [w]rite the patch to a file (patch.txt) ") + response = raw_input("[s]kip this patch / [a]pply the patch forcibly " + "and with .rej files / [w]rite the patch to a file (patch.txt) ") if response == "s": print "Skipping! Good luck with the next patches..." return @@ -309,11 +312,13 @@ class P4Submit(Command): if len(filesToDelete): print "The following files should be scheduled for deletion with p4 delete:" print " ".join(filesToDelete) - die("Please resolve and submit the conflict manually and continue afterwards with git-p4 submit --continue") + die("Please resolve and submit the conflict manually and " + + "continue afterwards with git-p4 submit --continue") elif response == "w": system(diffcmd + " > patch.txt") print "Patch saved to patch.txt in %s !" % self.clientPath - die("Please resolve and submit the conflict manually and continue afterwards with git-p4 submit --continue") + die("Please resolve and submit the conflict manually and " + "continue afterwards with git-p4 submit --continue") system(applyPatchCmd) @@ -407,7 +412,9 @@ class P4Submit(Command): file = open(fileName, "w+") file.write(self.prepareLogMessage(template, logMessage)) file.close() - print "Perforce submit template written as %s. Please review/edit and then use p4 submit -i < %s to submit directly!" % (fileName, fileName) + print ("Perforce submit template written as %s. " + + "Please review/edit and then use p4 submit -i < %s to submit directly!" + % (fileName, fileName)) def run(self, args): global gitdir @@ -634,7 +641,6 @@ class P4Sync(Command): for file in files: path = file["path"] if not path.startswith(branchPrefix): - # if not silent: # print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) continue rev = file["rev"] @@ -701,11 +707,13 @@ class P4Sync(Command): else: if not self.silent: - print "Tag %s does not match with change %s: files do not match." % (labelDetails["label"], change) + print ("Tag %s does not match with change %s: files do not match." + % (labelDetails["label"], change)) else: if not self.silent: - print "Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change) + print ("Tag %s does not match with change %s: file count is different." + % (labelDetails["label"], change)) def getUserMapFromPerforceServer(self): if self.userMapFromPerforceServer: @@ -854,11 +862,10 @@ class P4Sync(Command): else: self.refPrefix = "refs/heads/" - if self.syncWithOrigin: - if self.hasOrigin: - if not self.silent: - print "Syncing with origin first by calling git fetch origin" - system("git fetch origin") + if self.syncWithOrigin and self.hasOrigin: + if not self.silent: + print "Syncing with origin first by calling git fetch origin" + system("git fetch origin") if len(self.branch) == 0: self.branch = self.refPrefix + "master" @@ -884,7 +891,8 @@ class P4Sync(Command): p4Change = 0 for branch in self.p4BranchesInGit: - depotPath, change = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(self.refPrefix + branch)) + logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch) + (depotPath, change) = extractDepotPathAndChangeFromGitLog(logMsg) if self.verbose: print "path %s change %s" % (depotPath, change) @@ -922,7 +930,8 @@ class P4Sync(Command): return False else: if len(self.depotPath) != 0 and self.depotPath != args[0]: - print "previous import used depot path %s and now %s was specified. this doesn't work!" % (self.depotPath, args[0]) + print ("previous import used depot path %s and now %s was specified. " + "This doesn't work!" % (self.depotPath, args[0])) sys.exit(1) self.depotPath = args[0] @@ -968,7 +977,8 @@ class P4Sync(Command): self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) - importProcess = subprocess.Popen(["git", "fast-import"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE); + importProcess = subprocess.Popen(["git", "fast-import"], + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE); self.gitOutput = importProcess.stdout self.gitStream = importProcess.stdin self.gitError = importProcess.stderr @@ -977,7 +987,8 @@ class P4Sync(Command): print "Doing initial import of %s from revision %s" % (self.depotPath, self.revision) details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = "Initial import of %s from the state at revision %s" % (self.depotPath, self.revision) + details["desc"] = ("Initial import of %s from the state at revision %s" + % (self.depotPath, self.revision)) details["change"] = self.revision newestRevision = 0 @@ -1123,7 +1134,8 @@ class P4Rebase(Command): def __init__(self): Command.__init__(self) self.options = [ ] - self.description = "Fetches the latest revision from perforce and rebases the current work (branch) against it" + self.description = ("Fetches the latest revision from perforce and " + + "rebases the current work (branch) against it") def run(self, args): sync = P4Sync() -- cgit v1.2.3 From 7cb5cbefd2c8b4c24bc87c6e30906907f94726ad Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 16:55:48 -0300 Subject: rename apply() to applyCommit(); apply is a python builtin Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index aba4752d4e..bd0ea54929 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -259,7 +259,7 @@ class P4Submit(Command): return result - def apply(self, id): + def applyCommit(self, id): if self.directSubmit: print "Applying local change in working directory/index" diff = self.diffStatus @@ -494,7 +494,7 @@ class P4Submit(Command): commit = commits[0] commits = commits[1:] self.config["commits"] = commits - self.apply(commit) + self.applyCommit(commit) if not self.interactive: break -- cgit v1.2.3 From c8cbbee980ac3961d5cdae164f2cb0a6b88075e0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 28 May 2007 14:43:25 +0200 Subject: Fix my email address, this isn't really KDE related :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bd0ea54929..400edce459 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -2,8 +2,8 @@ # # git-p4.py -- A tool for bidirectional operation between a Perforce depot and git. # -# Author: Simon Hausmann -# Copyright: 2007 Simon Hausmann +# Author: Simon Hausmann +# Copyright: 2007 Simon Hausmann # 2007 Trolltech ASA # License: MIT # -- cgit v1.2.3 From b016d39756f7d82e51c35e281673ed30b95cc586 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 17:10:46 -0300 Subject: Robustness fixes for pipes - add read_pipe(), read_pipe_lines(), write_pipe(), which check pipe.close() - use throughout Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 85 +++++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 27 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 400edce459..05b308f5be 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -14,9 +14,43 @@ import re from sets import Set; gitdir = os.environ.get("GIT_DIR", "") +silent = False -def mypopen(command): - return os.popen(command, "rb"); +def write_pipe (c, str): + if not silent: + sys.stderr.write ('writing pipe: %s\n' % c) + + ## todo: check return status + pipe = os.popen (c, 'w') + val = pipe.write(str) + if pipe.close (): + sys.stderr.write ('Command failed') + sys.exit (1) + + return val + +def read_pipe (c): + sys.stderr.write ('reading pipe: %s\n' % c) + ## todo: check return status + pipe = os.popen (c, 'rb') + val = pipe.read() + if pipe.close (): + sys.stderr.write ('Command failed') + sys.exit (1) + + return val + + +def read_pipe_lines (c): + sys.stderr.write ('reading pipe: %s\n' % c) + ## todo: check return status + pipe = os.popen (c, 'rb') + val = pipe.readlines() + if pipe.close (): + sys.stderr.write ('Command failed') + sys.exit (1) + + return val def p4CmdList(cmd): cmd = "p4 -G %s" % cmd @@ -67,7 +101,7 @@ def die(msg): sys.exit(1) def currentGitBranch(): - return mypopen("git name-rev HEAD").read().split(" ")[1][:-1] + return read_pipe("git name-rev HEAD").split(" ")[1][:-1] def isValidGitDir(path): if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): @@ -75,7 +109,7 @@ def isValidGitDir(path): return False def parseRevision(ref): - return mypopen("git rev-parse %s" % ref).read()[:-1] + return read_pipe("git rev-parse %s" % ref)[:-1] def system(cmd): if os.system(cmd) != 0: @@ -83,8 +117,10 @@ def system(cmd): def extractLogMessageFromGitCommit(commit): logMessage = "" + + ## fixme: title is first line of commit, not 1st paragraph. foundTitle = False - for log in mypopen("git cat-file commit %s" % commit).readlines(): + for log in read_pipe_lines("git cat-file commit %s" % commit): if not foundTitle: if len(log) == 1: foundTitle = True @@ -158,10 +194,10 @@ class P4RollBack(Command): if self.rollbackLocalBranches: refPrefix = "refs/heads/" - lines = mypopen("git rev-parse --symbolic --branches").readlines() + lines = read_pipe_lines("git rev-parse --symbolic --branches") else: refPrefix = "refs/remotes/" - lines = mypopen("git rev-parse --symbolic --remotes").readlines() + lines = read_pipe_lines("git rev-parse --symbolic --remotes") for line in lines: if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"): @@ -230,7 +266,7 @@ class P4Submit(Command): if self.directSubmit: commits.append("0") else: - for line in mypopen("git rev-list --no-merges %s..%s" % (self.origin, self.master)).readlines(): + for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): commits.append(line[:-1]) commits.reverse() @@ -264,8 +300,8 @@ class P4Submit(Command): print "Applying local change in working directory/index" diff = self.diffStatus else: - print "Applying %s" % (mypopen("git log --max-count=1 --pretty=oneline %s" % id).read()) - diff = mypopen("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)).readlines() + print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) + diff = read_pipe_lines("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)) filesToAdd = set() filesToDelete = set() editedFiles = set() @@ -334,11 +370,11 @@ class P4Submit(Command): logMessage = logMessage.replace("\n", "\n\t") logMessage = logMessage[:-1] - template = mypopen("p4 change -o").read() + template = read_pipe("p4 change -o") if self.interactive: submitTemplate = self.prepareLogMessage(template, logMessage) - diff = mypopen("p4 diff -du ...").read() + diff = read_pipe("p4 diff -du ...") for newFile in filesToAdd: diff += "==== new file ====\n" @@ -387,14 +423,10 @@ class P4Submit(Command): if self.directSubmit: print "Submitting to git first" os.chdir(self.oldWorkingDirectory) - pipe = os.popen("git commit -a -F -", "wb") - pipe.write(submitTemplate) - pipe.close() + write_pipe("git commit -a -F -", submitTemplate) os.chdir(self.clientPath) - pipe = os.popen("p4 submit -i", "wb") - pipe.write(submitTemplate) - pipe.close() + write_pipe("p4 submit -i", submitTemplate) elif response == "s": for f in editedFiles: system("p4 revert \"%s\"" % f); @@ -451,11 +483,11 @@ class P4Submit(Command): self.oldWorkingDirectory = os.getcwd() if self.directSubmit: - self.diffStatus = mypopen("git diff -r --name-status HEAD").readlines() + self.diffStatus = read_pipe_lines("git diff -r --name-status HEAD") if len(self.diffStatus) == 0: print "No changes in working directory to submit." return True - patch = mypopen("git diff -p --binary --diff-filter=ACMRTUXB HEAD").read() + patch = read_pipe("git diff -p --binary --diff-filter=ACMRTUXB HEAD") self.diffFile = gitdir + "/p4-git-diff" f = open(self.diffFile, "wb") f.write(patch) @@ -557,7 +589,7 @@ class P4Sync(Command): self.syncWithOrigin = False def p4File(self, depotPath): - return os.popen("p4 print -q \"%s\"" % depotPath, "rb").read() + return read_pipe("p4 print -q \"%s\"" % depotPath) def extractFilesFromCommit(self, commit): files = [] @@ -799,7 +831,7 @@ class P4Sync(Command): else: cmdline += " --branches" - for line in mypopen(cmdline).readlines(): + for line in read_pipe_lines(cmdline): if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD\n"): continue if self.importIntoRemotes: @@ -1032,7 +1064,7 @@ class P4Sync(Command): else: if self.verbose: print "Getting p4 changes for %s...%s" % (self.depotPath, self.changeRange) - output = mypopen("p4 changes %s...%s" % (self.depotPath, self.changeRange)).readlines() + output = read_pipe_lines("p4 changes %s...%s" % (self.depotPath, self.changeRange)) for line in output: changeNum = line.split(" ")[1] @@ -1141,7 +1173,7 @@ class P4Rebase(Command): sync = P4Sync() sync.run([]) print "Rebasing the current branch" - oldHead = mypopen("git rev-parse HEAD").read()[:-1] + oldHead = read_pipe("git rev-parse HEAD")[:-1] system("git rebase p4") system("git diff-tree --stat --summary -M %s HEAD" % oldHead) return True @@ -1252,9 +1284,9 @@ if cmd.needsGit: if len(gitdir) == 0: gitdir = ".git" if not isValidGitDir(gitdir): - gitdir = mypopen("git rev-parse --git-dir").read()[:-1] + gitdir = read_pipe("git rev-parse --git-dir")[:-1] if os.path.exists(gitdir): - cdup = mypopen("git rev-parse --show-cdup").read()[:-1]; + cdup = read_pipe("git rev-parse --show-cdup")[:-1]; if len(cdup) > 0: os.chdir(cdup); @@ -1268,4 +1300,3 @@ if cmd.needsGit: if not cmd.run(args): parser.print_help() - -- cgit v1.2.3 From bce4c5fc0b6aa9e599fc181c5350b461dafecee2 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 17:14:33 -0300 Subject: cleanup - use re.sub() iso. if for stripping ... - spacing nits Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 05b308f5be..8d649dd762 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -16,39 +16,39 @@ from sets import Set; gitdir = os.environ.get("GIT_DIR", "") silent = False -def write_pipe (c, str): +def write_pipe(c, str): if not silent: - sys.stderr.write ('writing pipe: %s\n' % c) + sys.stderr.write('writing pipe: %s\n' % c) ## todo: check return status - pipe = os.popen (c, 'w') + pipe = os.popen(c, 'w') val = pipe.write(str) - if pipe.close (): - sys.stderr.write ('Command failed') - sys.exit (1) + if pipe.close(): + sys.stderr.write('Command failed') + sys.exit(1) return val -def read_pipe (c): - sys.stderr.write ('reading pipe: %s\n' % c) +def read_pipe(c): + sys.stderr.write('reading pipe: %s\n' % c) ## todo: check return status - pipe = os.popen (c, 'rb') + pipe = os.popen(c, 'rb') val = pipe.read() - if pipe.close (): - sys.stderr.write ('Command failed') - sys.exit (1) + if pipe.close(): + sys.stderr.write('Command failed') + sys.exit(1) return val -def read_pipe_lines (c): - sys.stderr.write ('reading pipe: %s\n' % c) +def read_pipe_lines(c): + sys.stderr.write('reading pipe: %s\n' % c) ## todo: check return status - pipe = os.popen (c, 'rb') + pipe = os.popen(c, 'rb') val = pipe.readlines() - if pipe.close (): - sys.stderr.write ('Command failed') - sys.exit (1) + if pipe.close(): + sys.stderr.write('Command failed') + sys.exit(1) return val @@ -986,9 +986,7 @@ class P4Sync(Command): elif len(self.previousDepotPath) == 0: self.revision = "#head" - if self.depotPath.endswith("..."): - self.depotPath = self.depotPath[:-3] - + self.depotPath = re.sub ("\.\.\.$", "", self.depotPath) if not self.depotPath.endswith("/"): self.depotPath += "/" -- cgit v1.2.3 From 6754a299d8d977326e396d641106cc5db4d29db1 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 17:41:50 -0300 Subject: minor cleanups Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 8d649dd762..93c9d6417c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -30,7 +30,8 @@ def write_pipe(c, str): return val def read_pipe(c): - sys.stderr.write('reading pipe: %s\n' % c) + if not silent: + sys.stderr.write('reading pipe: %s\n' % c) ## todo: check return status pipe = os.popen(c, 'rb') val = pipe.read() @@ -42,7 +43,8 @@ def read_pipe(c): def read_pipe_lines(c): - sys.stderr.write('reading pipe: %s\n' % c) + if not silent: + sys.stderr.write('reading pipe: %s\n' % c) ## todo: check return status pipe = os.popen(c, 'rb') val = pipe.readlines() @@ -52,6 +54,14 @@ def read_pipe_lines(c): return val +def system(cmd): + if not silent: + sys.stderr.write("executing %s" % cmd) + if os.system(cmd) != 0: + die("command failed: %s" % cmd) + + + def p4CmdList(cmd): cmd = "p4 -G %s" % cmd pipe = os.popen(cmd, "rb") @@ -111,10 +121,6 @@ def isValidGitDir(path): def parseRevision(ref): return read_pipe("git rev-parse %s" % ref)[:-1] -def system(cmd): - if os.system(cmd) != 0: - die("command failed: %s" % cmd) - def extractLogMessageFromGitCommit(commit): logMessage = "" @@ -571,7 +577,6 @@ class P4Sync(Command): (a ... is not needed in the path p4 specification, it's added implicitly)""" self.usage += " //depot/path[@revRange]" - self.silent = False self.createdBranches = Set() self.committedChanges = Set() @@ -584,6 +589,7 @@ class P4Sync(Command): self.importIntoRemotes = True self.maxChanges = "" self.isWindows = (platform.system() == "Windows") + self.depotPath = None if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False @@ -611,9 +617,11 @@ class P4Sync(Command): fnum = fnum + 1 return files + def stripRepoPath(self, path): + return path[len(self.depotPath):] + def splitFilesIntoBranches(self, commit): branches = {} - fnum = 0 while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] @@ -630,10 +638,12 @@ class P4Sync(Command): file["type"] = commit["type%s" % fnum] fnum = fnum + 1 - relPath = path[len(self.depotPath):] + relPath = self.stripRepoPath(path) for branch in self.knownBranches.keys(): - if relPath.startswith(branch + "/"): # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2 + + # add a trailing slash so that a commit into qt/4.2foo doesn't end up in qt/4.2 + if relPath.startswith(branch + "/"): if branch not in branches: branches[branch] = [] branches[branch].append(file) -- cgit v1.2.3 From 8b41a97f8a3e2778ce733cd3d7e181bc28cc43a0 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:20:53 -0300 Subject: clone and sync --keep-path to keep perforce path to module. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 93c9d6417c..51d117b789 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -11,6 +11,7 @@ import optparse, sys, os, marshal, popen2, subprocess, shelve import tempfile, getopt, sha, os.path, time, platform import re + from sets import Set; gitdir = os.environ.get("GIT_DIR", "") @@ -20,7 +21,6 @@ def write_pipe(c, str): if not silent: sys.stderr.write('writing pipe: %s\n' % c) - ## todo: check return status pipe = os.popen(c, 'w') val = pipe.write(str) if pipe.close(): @@ -32,7 +32,7 @@ def write_pipe(c, str): def read_pipe(c): if not silent: sys.stderr.write('reading pipe: %s\n' % c) - ## todo: check return status + pipe = os.popen(c, 'rb') val = pipe.read() if pipe.close(): @@ -60,8 +60,6 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) - - def p4CmdList(cmd): cmd = "p4 -G %s" % cmd pipe = os.popen(cmd, "rb") @@ -566,7 +564,8 @@ class P4Sync(Command): optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false"), - optparse.make_option("--max-changes", dest="maxChanges") + optparse.make_option("--max-changes", dest="maxChanges"), + optparse.make_option("--keep-path", dest="keepRepoPath") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -590,6 +589,7 @@ class P4Sync(Command): self.maxChanges = "" self.isWindows = (platform.system() == "Windows") self.depotPath = None + self.keepRepoPath = False if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False @@ -617,8 +617,11 @@ class P4Sync(Command): fnum = fnum + 1 return files - def stripRepoPath(self, path): - return path[len(self.depotPath):] + def stripRepoPath(self, path, prefix): + if self.keepRepoPath: + prefix = re.sub("^(//[^/]+/).*", r'\1', prefix) + + return path[len(prefix):] def splitFilesIntoBranches(self, commit): branches = {} @@ -638,7 +641,7 @@ class P4Sync(Command): file["type"] = commit["type%s" % fnum] fnum = fnum + 1 - relPath = self.stripRepoPath(path) + relPath = self.stripRepoPath(path, self.depotPath) for branch in self.knownBranches.keys(): @@ -687,7 +690,7 @@ class P4Sync(Command): continue rev = file["rev"] depotPath = path + "#" + rev - relPath = path[len(branchPrefix):] + relPath = self.stripRepoPath(path, branchPrefix) action = file["action"] if file["type"] == "apple": -- cgit v1.2.3 From b76f0565bfcfedba9e8920aa5120fc97796b642f Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:29:34 -0300 Subject: use string.strip() iso. slicing. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 51d117b789..ac446a8dc4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -845,15 +845,15 @@ class P4Sync(Command): cmdline += " --branches" for line in read_pipe_lines(cmdline): + lie = line.strip() if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD\n"): continue if self.importIntoRemotes: # strip off p4 - branch = line[3:-1] - else: - branch = line[:-1] + branch = re.sub ("^p4/", "", line) + self.p4BranchesInGit.append(branch) - self.initialParents[self.refPrefix + branch] = parseRevision(line[:-1]) + self.initialParents[self.refPrefix + branch] = parseRevision(line) def createOrUpdateBranchesFromOrigin(self): if not self.silent: -- cgit v1.2.3 From b25b20656dceff6145a1286f10618ab25d717537 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: use strip() iso. slicing for removing \n Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ac446a8dc4..efa2fce29e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -15,7 +15,7 @@ import re from sets import Set; gitdir = os.environ.get("GIT_DIR", "") -silent = False +silent = True def write_pipe(c, str): if not silent: @@ -109,7 +109,7 @@ def die(msg): sys.exit(1) def currentGitBranch(): - return read_pipe("git name-rev HEAD").split(" ")[1][:-1] + return read_pipe("git name-rev HEAD").split(" ")[1].strip() def isValidGitDir(path): if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): @@ -117,7 +117,7 @@ def isValidGitDir(path): return False def parseRevision(ref): - return read_pipe("git rev-parse %s" % ref)[:-1] + return read_pipe("git rev-parse %s" % ref).strip() def extractLogMessageFromGitCommit(commit): logMessage = "" @@ -205,7 +205,8 @@ class P4RollBack(Command): for line in lines: if self.rollbackLocalBranches or (line.startswith("p4/") and line != "p4/HEAD\n"): - ref = refPrefix + line[:-1] + line = line.strip() + ref = refPrefix + line log = extractLogMessageFromGitCommit(ref) depotPath, change = extractDepotPathAndChangeFromGitLog(log) changed = False @@ -271,7 +272,7 @@ class P4Submit(Command): commits.append("0") else: for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): - commits.append(line[:-1]) + commits.append(line.strip()) commits.reverse() self.config["commits"] = commits @@ -372,7 +373,7 @@ class P4Submit(Command): if not self.directSubmit: logMessage = extractLogMessageFromGitCommit(id) logMessage = logMessage.replace("\n", "\n\t") - logMessage = logMessage[:-1] + logMessage = logMessage.strip() template = read_pipe("p4 change -o") @@ -513,7 +514,7 @@ class P4Submit(Command): if len(self.substFile) > 0: for line in open(self.substFile, "r").readlines(): - tokens = line[:-1].split("=") + tokens = line.strip().split("=") self.logSubstitutions[tokens[0]] = tokens[1] self.check() @@ -784,7 +785,7 @@ class P4Sync(Command): lines = cache.readlines() cache.close() for line in lines: - entry = line[:-1].split("\t") + entry = line.strip().split("\t") self.users[entry[0]] = entry[1] except IOError: self.getUserMapFromPerforceServer() @@ -814,7 +815,7 @@ class P4Sync(Command): print "Label changes: %s" % self.labels.keys() def getBranchMapping(self): - self.projectName = self.depotPath[self.depotPath[:-1].rfind("/") + 1:] + self.projectName = self.depotPath[self.depotPath.strip().rfind("/") + 1:] for info in p4CmdList("branches"): details = p4Cmd("branch -o %s" % info["branch"]) @@ -848,6 +849,7 @@ class P4Sync(Command): lie = line.strip() if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD\n"): continue + if self.importIntoRemotes: # strip off p4 branch = re.sub ("^p4/", "", line) @@ -966,7 +968,7 @@ class P4Sync(Command): self.branch = "refs/heads/" + self.branch if len(self.depotPath) != 0: - self.depotPath = self.depotPath[:-1] + self.depotPath = self.depotPath.strip() if len(args) == 0 and len(self.depotPath) != 0: if not self.silent: @@ -1184,7 +1186,7 @@ class P4Rebase(Command): sync = P4Sync() sync.run([]) print "Rebasing the current branch" - oldHead = read_pipe("git rev-parse HEAD")[:-1] + oldHead = read_pipe("git rev-parse HEAD").strip() system("git rebase p4") system("git diff-tree --stat --summary -M %s HEAD" % oldHead) return True @@ -1217,7 +1219,7 @@ class P4Clone(P4Sync): depotDir = re.sub(r"/$", "", depotDir) if not destination: - destination = os.path.split(depotDir)[-1] + destination = os.path.split(depotDir)[1] print "Importing from %s into %s" % (depotPath, destination) os.makedirs(destination) @@ -1295,9 +1297,9 @@ if cmd.needsGit: if len(gitdir) == 0: gitdir = ".git" if not isValidGitDir(gitdir): - gitdir = read_pipe("git rev-parse --git-dir")[:-1] + gitdir = read_pipe("git rev-parse --git-dir").strip() if os.path.exists(gitdir): - cdup = read_pipe("git rev-parse --show-cdup")[:-1]; + cdup = read_pipe("git rev-parse --show-cdup").strip() if len(cdup) > 0: os.chdir(cdup); -- cgit v1.2.3 From 4addad229169ece62f36c6c17cc15bdf532a60bc Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: add --verbose to all commands. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index efa2fce29e..cf9db73da6 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -15,47 +15,47 @@ import re from sets import Set; gitdir = os.environ.get("GIT_DIR", "") -silent = True +verbose = False def write_pipe(c, str): - if not silent: + if verbose: sys.stderr.write('writing pipe: %s\n' % c) pipe = os.popen(c, 'w') val = pipe.write(str) if pipe.close(): - sys.stderr.write('Command failed') + sys.stderr.write('Command %s failed\n' % c) sys.exit(1) return val -def read_pipe(c): - if not silent: +def read_pipe(c, ignore_error=False): + if verbose: sys.stderr.write('reading pipe: %s\n' % c) pipe = os.popen(c, 'rb') val = pipe.read() - if pipe.close(): - sys.stderr.write('Command failed') + if pipe.close() and not ignore_error: + sys.stderr.write('Command %s failed\n' % c) sys.exit(1) return val def read_pipe_lines(c): - if not silent: + if verbose: sys.stderr.write('reading pipe: %s\n' % c) ## todo: check return status pipe = os.popen(c, 'rb') val = pipe.readlines() if pipe.close(): - sys.stderr.write('Command failed') + sys.stderr.write('Command %s failed\n' % c) sys.exit(1) return val def system(cmd): - if not silent: + if verbose: sys.stderr.write("executing %s" % cmd) if os.system(cmd) != 0: die("command failed: %s" % cmd) @@ -157,7 +157,7 @@ def gitBranchExists(branch): return proc.wait() == 0; def gitConfig(key): - return mypopen("git config %s" % key).read()[:-1] + return read_pipe("git config %s" % key, ignore_error=True).strip() class Command: def __init__(self): @@ -168,7 +168,8 @@ class P4Debug(Command): def __init__(self): Command.__init__(self) self.options = [ - ] + optparse.make_option("--verbose", dest="verbose", action="store_true"), + ] self.description = "A tool to debug the output of p4 -G." self.needsGit = False @@ -234,6 +235,7 @@ class P4Submit(Command): Command.__init__(self) self.options = [ optparse.make_option("--continue", action="store_false", dest="firstTime"), + optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), optparse.make_option("--log-substitutions", dest="substFile"), @@ -861,11 +863,12 @@ class P4Sync(Command): if not self.silent: print "Creating/updating branch(es) in %s based on origin branch(es)" % self.refPrefix - for line in mypopen("git rev-parse --symbolic --remotes"): + for line in read_pipe_lines("git rev-parse --symbolic --remotes"): + line = line.strip() if (not line.startswith("origin/")) or line.endswith("HEAD\n"): continue - headName = line[len("origin/"):-1] + headName = line[len("origin/")] remoteHead = self.refPrefix + headName originHead = "origin/" + headName @@ -1292,6 +1295,7 @@ if len(options) > 0: (cmd, args) = parser.parse_args(sys.argv[2:], cmd); +verbose = cmd.verbose if cmd.needsGit: gitdir = cmd.gitdir if len(gitdir) == 0: -- cgit v1.2.3 From 6326aa58662932212678a25c6f482a8da4195ac9 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: Extract multiple paths concurrently. This enables importing just the interesting bits of large repositories. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 262 ++++++++++++++++++++++++++------------------- 1 file changed, 153 insertions(+), 109 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index cf9db73da6..ad145e8595 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -133,24 +133,26 @@ def extractLogMessageFromGitCommit(commit): logMessage += log return logMessage -def extractDepotPathAndChangeFromGitLog(log): +def extractDepotPathsAndChangeFromGitLog(log): values = {} for line in log.split("\n"): line = line.strip() - if line.startswith("[git-p4:") and line.endswith("]"): - line = line[8:-1].strip() - for assignment in line.split(":"): - variable = assignment.strip() - value = "" - equalPos = assignment.find("=") - if equalPos != -1: - variable = assignment[:equalPos].strip() - value = assignment[equalPos + 1:].strip() - if value.startswith("\"") and value.endswith("\""): - value = value[1:-1] - values[variable] = value - - return values.get("depot-path"), values.get("change") + m = re.search (r"^ *\[git-p4: (.*)\]$", line) + if not m: + continue + + assignments = m.group(1).split (':') + for a in assignments: + vals = a.split ('=') + key = vals[0].strip() + val = ('='.join (vals[1:])).strip() + if val.endswith ('\"') and val.startswith('"'): + val = val[1:-1] + + values[key] = val + + paths = values.get("depot-path").split(',') + return paths, values.get("change") def gitBranchExists(branch): proc = subprocess.Popen(["git", "rev-parse", branch], stderr=subprocess.PIPE, stdout=subprocess.PIPE); @@ -209,10 +211,11 @@ class P4RollBack(Command): line = line.strip() ref = refPrefix + line log = extractLogMessageFromGitCommit(ref) - depotPath, change = extractDepotPathAndChangeFromGitLog(log) + depotPaths, change = extractDepotPathsAndChangeFromGitLog(log) changed = False - if len(p4Cmd("changes -m 1 %s...@%s" % (depotPath, maxChange))) == 0: + if len(p4Cmd("changes -m 1 " + ' '.join (['%s...@%s' % (p, maxChange) + for p in depotPaths]))) == 0: print "Branch %s did not exist at change %s, deleting." % (ref, maxChange) system("git update-ref -d %s `git rev-parse %s`" % (ref, ref)) continue @@ -223,7 +226,7 @@ class P4RollBack(Command): print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange) system("git update-ref %s \"%s^\"" % (ref, ref)) log = extractLogMessageFromGitCommit(ref) - depotPath, change = extractDepotPathAndChangeFromGitLog(log) + depotPaths, change = extractDepotPathsAndChangeFromGitLog(log) if changed: print "%s rewound to %s" % (ref, change) @@ -472,9 +475,9 @@ class P4Submit(Command): depotPath = "" if gitBranchExists("p4"): - [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) + [depotPaths, dummy] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) if len(depotPath) == 0 and gitBranchExists("origin"): - [depotPath, dummy] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) + [depotPaths, dummy] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) if len(depotPath) == 0: print "Internal error: cannot locate perforce depot path from existing branches" @@ -568,7 +571,7 @@ class P4Sync(Command): optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false"), optparse.make_option("--max-changes", dest="maxChanges"), - optparse.make_option("--keep-path", dest="keepRepoPath") + optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true') ] self.description = """Imports from Perforce into a git repository.\n example: @@ -591,8 +594,8 @@ class P4Sync(Command): self.importIntoRemotes = True self.maxChanges = "" self.isWindows = (platform.system() == "Windows") - self.depotPath = None self.keepRepoPath = False + self.depotPaths = None if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False @@ -605,9 +608,10 @@ class P4Sync(Command): fnum = 0 while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] - if not path.startswith(self.depotPath): - # if not self.silent: - # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.depotPath, change) + + found = [p for p in self.depotPaths + if path.startswith (p)] + if not found: fnum = fnum + 1 continue @@ -620,20 +624,24 @@ class P4Sync(Command): fnum = fnum + 1 return files - def stripRepoPath(self, path, prefix): + def stripRepoPath(self, path, prefixes): if self.keepRepoPath: - prefix = re.sub("^(//[^/]+/).*", r'\1', prefix) + prefixes = [re.sub("^(//[^/]+/).*", r'\1', prefixes[0])] + + for p in prefixes: + if path.startswith(p): + path = path[len(p):] - return path[len(prefix):] + return path def splitFilesIntoBranches(self, commit): branches = {} fnum = 0 while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] - if not path.startswith(self.depotPath): - # if not self.silent: - # print "\nchanged files: ignoring path %s outside of %s in change %s" % (path, self.depotPath, change) + found = [p for p in self.depotPaths + if path.startswith (p)] + if not found: fnum = fnum + 1 continue @@ -644,7 +652,7 @@ class P4Sync(Command): file["type"] = commit["type%s" % fnum] fnum = fnum + 1 - relPath = self.stripRepoPath(path, self.depotPath) + relPath = self.stripRepoPath(path, self.depotPaths) for branch in self.knownBranches.keys(): @@ -656,7 +664,7 @@ class P4Sync(Command): return branches - def commit(self, details, files, branch, branchPrefix, parent = ""): + def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] author = details["user"] @@ -678,7 +686,8 @@ class P4Sync(Command): self.gitStream.write("data < 0: @@ -688,12 +697,13 @@ class P4Sync(Command): for file in files: path = file["path"] - if not path.startswith(branchPrefix): - # print "\nchanged files: ignoring path %s outside of branch prefix %s in change %s" % (path, branchPrefix, details["change"]) + + + if not [p for p in branchPrefixes if path.startswith(p)]: continue rev = file["rev"] depotPath = path + "#" + rev - relPath = self.stripRepoPath(path, branchPrefix) + relPath = self.stripRepoPath(path, branchPrefixes) action = file["action"] if file["type"] == "apple": @@ -728,7 +738,8 @@ class P4Sync(Command): if self.verbose: print "Change %s is labelled %s" % (change, labelDetails) - files = p4CmdList("files %s...@%s" % (branchPrefix, change)) + files = p4CmdList("files " + ' '.join (["%s...@%s" % (p, change) + for p in branchPrefixes])) if len(files) == len(labelRevisions): @@ -795,9 +806,9 @@ class P4Sync(Command): def getLabels(self): self.labels = {} - l = p4CmdList("labels %s..." % self.depotPath) + l = p4CmdList("labels %s..." % ' '.join (self.depotPaths)) if len(l) > 0 and not self.silent: - print "Finding files belonging to labels in %s" % self.depotPath + print "Finding files belonging to labels in %s" % `self.depotPath` for output in l: label = output["label"] @@ -805,7 +816,9 @@ class P4Sync(Command): newestChange = 0 if self.verbose: print "Querying files for label %s" % label - for file in p4CmdList("files %s...@%s" % (self.depotPath, label)): + for file in p4CmdList("files " + + ' '.join (["%s...@%s" % (p, label) + for p in self.depotPaths])): revisions[file["depotFile"]] = file["rev"] change = int(file["change"]) if change > newestChange: @@ -817,6 +830,8 @@ class P4Sync(Command): print "Label changes: %s" % self.labels.keys() def getBranchMapping(self): + + ## FIXME - what's a P4 projectName ? self.projectName = self.depotPath[self.depotPath.strip().rfind("/") + 1:] for info in p4CmdList("branches"): @@ -872,8 +887,8 @@ class P4Sync(Command): remoteHead = self.refPrefix + headName originHead = "origin/" + headName - [originPreviousDepotPath, originP4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead)) - if len(originPreviousDepotPath) == 0 or len(originP4Change) == 0: + [originPreviousDepotPaths, originP4Change] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead)) + if len(originPreviousDepotPaths) == 0 or len(originP4Change) == 0: continue update = False @@ -882,25 +897,26 @@ class P4Sync(Command): print "creating %s" % remoteHead update = True else: - [p4PreviousDepotPath, p4Change] = extractDepotPathAndChangeFromGitLog(extractLogMessageFromGitCommit(remoteHead)) + [p4PreviousDepotPaths, p4Change] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit(remoteHead)) if len(p4Change) > 0: - if originPreviousDepotPath == p4PreviousDepotPath: + if originPreviousDepotPaths == p4PreviousDepotPaths: originP4Change = int(originP4Change) p4Change = int(p4Change) if originP4Change > p4Change: print "%s (%s) is newer than %s (%s). Updating p4 branch from origin." % (originHead, originP4Change, remoteHead, p4Change) update = True else: - print "Ignoring: %s was imported from %s while %s was imported from %s" % (originHead, originPreviousDepotPath, remoteHead, p4PreviousDepotPath) + print "Ignoring: %s was imported from %s while %s was imported from %s" % (originHead, originPreviousDepotPaths, remoteHead, p4PreviousDepotPaths) if update: system("git update-ref %s %s" % (remoteHead, originHead)) + def run(self, args): - self.depotPath = "" + self.depotPaths = [] self.changeRange = "" self.initialParent = "" - self.previousDepotPath = "" + self.previousDepotPaths = [] # map from branch depot path to parent branch self.knownBranches = {} @@ -926,7 +942,7 @@ class P4Sync(Command): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) - if len(args) == 0: + if args == []: if self.hasOrigin: self.createOrUpdateBranchesFromOrigin() self.listExistingP4GitBranches() @@ -942,26 +958,31 @@ class P4Sync(Command): p4Change = 0 for branch in self.p4BranchesInGit: logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch) - (depotPath, change) = extractDepotPathAndChangeFromGitLog(logMsg) + (depotPaths, change) = extractDepotPathsAndChangeFromGitLog(logMsg) if self.verbose: - print "path %s change %s" % (depotPath, change) + print "path %s change %s" % (','.join(depotPaths), change) - if len(depotPath) > 0 and len(change) > 0: + if len(depotPaths) > 0 and len(change) > 0: change = int(change) + 1 p4Change = max(p4Change, change) - if len(self.previousDepotPath) == 0: - self.previousDepotPath = depotPath + if len(self.previousDepotPaths) == 0: + self.previousDepotPaths = depotPaths else: - i = 0 - l = min(len(self.previousDepotPath), len(depotPath)) - while i < l and self.previousDepotPath[i] == depotPath[i]: - i = i + 1 - self.previousDepotPath = self.previousDepotPath[:i] + ## FIXME + paths = [] + for (prev, cur) in zip(self.previousDepotPaths, depotPaths): + for i in range(0, max(len(cur), len(prev))): + if cur[i] <> prev[i]: + break + + paths.append (cur[:i]) + + self.previousDepotPaths = paths if p4Change > 0: - self.depotPath = self.previousDepotPath + self.depotPaths = self.previousDepotPaths self.changeRange = "@%s,#head" % p4Change self.initialParent = parseRevision(self.branch) if not self.silent and not self.detectBranches: @@ -970,43 +991,47 @@ class P4Sync(Command): if not self.branch.startswith("refs/"): self.branch = "refs/heads/" + self.branch - if len(self.depotPath) != 0: - self.depotPath = self.depotPath.strip() - - if len(args) == 0 and len(self.depotPath) != 0: + if len(args) == 0 and self.depotPaths: if not self.silent: - print "Depot path: %s" % self.depotPath - elif len(args) != 1: - return False + print "Depot paths: %s" % ' '.join(self.depotPaths) else: - if len(self.depotPath) != 0 and self.depotPath != args[0]: + if self.depotPaths and self.depotPaths != args: print ("previous import used depot path %s and now %s was specified. " - "This doesn't work!" % (self.depotPath, args[0])) + "This doesn't work!" % (' '.join (self.depotPaths), + ' '.join (args))) sys.exit(1) - self.depotPath = args[0] + + self.depotPaths = args self.revision = "" self.users = {} - if self.depotPath.find("@") != -1: - atIdx = self.depotPath.index("@") - self.changeRange = self.depotPath[atIdx:] - if self.changeRange == "@all": - self.changeRange = "" - elif self.changeRange.find(",") == -1: - self.revision = self.changeRange - self.changeRange = "" - self.depotPath = self.depotPath[0:atIdx] - elif self.depotPath.find("#") != -1: - hashIdx = self.depotPath.index("#") - self.revision = self.depotPath[hashIdx:] - self.depotPath = self.depotPath[0:hashIdx] - elif len(self.previousDepotPath) == 0: - self.revision = "#head" - - self.depotPath = re.sub ("\.\.\.$", "", self.depotPath) - if not self.depotPath.endswith("/"): - self.depotPath += "/" + newPaths = [] + for p in self.depotPaths: + if p.find("@") != -1: + atIdx = p.index("@") + self.changeRange = p[atIdx:] + if self.changeRange == "@all": + self.changeRange = "" + elif self.changeRange.find(",") == -1: + self.revision = self.changeRange + self.changeRange = "" + p = p[0:atIdx] + elif p.find("#") != -1: + hashIdx = p.index("#") + self.revision = p[hashIdx:] + p = p[0:hashIdx] + elif self.previousDepotPaths == []: + self.revision = "#head" + + p = re.sub ("\.\.\.$", "", p) + if not p.endswith("/"): + p += "/" + + newPaths.append(p) + + self.depotPaths = newPaths + self.loadUserMapFromCache() self.labels = {} @@ -1020,28 +1045,34 @@ class P4Sync(Command): print "initial parents: %s" % self.initialParents for b in self.p4BranchesInGit: if b != "master": + + ## FIXME b = b[len(self.projectName):] self.createdBranches.add(b) self.tz = "%+03d%02d" % (- time.timezone / 3600, ((- time.timezone % 3600) / 60)) importProcess = subprocess.Popen(["git", "fast-import"], - stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE); + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE); self.gitOutput = importProcess.stdout self.gitStream = importProcess.stdin self.gitError = importProcess.stderr if len(self.revision) > 0: - print "Doing initial import of %s from revision %s" % (self.depotPath, self.revision) + print "Doing initial import of %s from revision %s" % (' '.join(self.depotPaths), self.revision) details = { "user" : "git perforce import user", "time" : int(time.time()) } details["desc"] = ("Initial import of %s from the state at revision %s" - % (self.depotPath, self.revision)) + % (' '.join(self.depotPaths), self.revision)) details["change"] = self.revision newestRevision = 0 fileCnt = 0 - for info in p4CmdList("files %s...%s" % (self.depotPath, self.revision)): + for info in p4CmdList("files " + + ' '.join(["%s...%s" + % (p, self.revision) + for p in self.depotPaths])): change = int(info["change"]) if change > newestRevision: newestRevision = change @@ -1059,7 +1090,7 @@ class P4Sync(Command): details["change"] = newestRevision try: - self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPath) + self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths) except IOError: print "IO error with git fast-import. Is your git version recent enough?" print self.gitError.read() @@ -1079,8 +1110,11 @@ class P4Sync(Command): changes.sort() else: if self.verbose: - print "Getting p4 changes for %s...%s" % (self.depotPath, self.changeRange) - output = read_pipe_lines("p4 changes %s...%s" % (self.depotPath, self.changeRange)) + print "Getting p4 changes for %s...%s" % (`self.depotPaths`, + self.changeRange) + assert self.depotPaths + output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange) + for p in self.depotPaths])) for line in output: changeNum = line.split(" ")[1] @@ -1111,7 +1145,8 @@ class P4Sync(Command): if self.detectBranches: branches = self.splitFilesIntoBranches(description) for branch in branches.keys(): - branchPrefix = self.depotPath + branch + "/" + ## HACK --hwn + branchPrefix = self.depotPaths[0] + branch + "/" parent = "" @@ -1134,11 +1169,14 @@ class P4Sync(Command): if branch == "main": branch = "master" else: + + ## FIXME branch = self.projectName + branch if parent == "main": parent = "master" elif len(parent) > 0: + ## FIXME parent = self.projectName + parent branch = self.refPrefix + branch @@ -1155,7 +1193,8 @@ class P4Sync(Command): self.commit(description, filesForCommit, branch, branchPrefix, parent) else: files = self.extractFilesFromCommit(description) - self.commit(description, files, self.branch, self.depotPath, self.initialParent) + self.commit(description, files, self.branch, self.depotPaths, + self.initialParent) self.initialParent = "" except IOError: print self.gitError.read() @@ -1206,30 +1245,35 @@ class P4Clone(P4Sync): if len(args) < 1: return False - depotPath = args[0] destination = "" - if len(args) == 2: + if self.keepRepoPath: + destination = args[-1] + args = args[:-1] + elif len(args) == 2: destination = args[1] elif len(args) > 2: return False - if not depotPath.startswith("//"): - return False - - depotDir = re.sub("(@[^@]*)$", "", depotPath) - depotDir = re.sub("(#[^#]*)$", "", depotDir) - depotDir = re.sub(r"\.\.\.$,", "", depotDir) - depotDir = re.sub(r"/$", "", depotDir) + depotPaths = args + for p in depotPaths: + if not p.startswith("//"): + return False if not destination: + depotPath = args[0] + depotDir = re.sub("(@[^@]*)$", "", depotPath) + depotDir = re.sub("(#[^#]*)$", "", depotDir) + depotDir = re.sub(r"\.\.\.$,", "", depotDir) + depotDir = re.sub(r"/$", "", depotDir) + destination = os.path.split(depotDir)[1] - print "Importing from %s into %s" % (depotPath, destination) + print "Importing from %s into %s" % (`depotPaths`, destination) os.makedirs(destination) os.chdir(destination) system("git init") gitdir = os.getcwd() + "/.git" - if not P4Sync.run(self, [depotPath]): + if not P4Sync.run(self, depotPaths): return False if self.branch != "master": if gitBranchExists("refs/remotes/p4/master"): -- cgit v1.2.3 From 9226c03c3279d66e2dea21a7a3e9f187e5ee9b9a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 28 May 2007 19:23:19 +0200 Subject: In *_pipe print the command that failed if it fails. Fixed old calls to mypopen. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index efa2fce29e..0f1285b39b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -24,7 +24,7 @@ def write_pipe(c, str): pipe = os.popen(c, 'w') val = pipe.write(str) if pipe.close(): - sys.stderr.write('Command failed') + sys.stderr.write('Command failed: %s' % c) sys.exit(1) return val @@ -36,7 +36,7 @@ def read_pipe(c): pipe = os.popen(c, 'rb') val = pipe.read() if pipe.close(): - sys.stderr.write('Command failed') + sys.stderr.write('Command failed: %s' % c) sys.exit(1) return val @@ -49,7 +49,7 @@ def read_pipe_lines(c): pipe = os.popen(c, 'rb') val = pipe.readlines() if pipe.close(): - sys.stderr.write('Command failed') + sys.stderr.write('Command failed: %s' % c) sys.exit(1) return val @@ -157,7 +157,7 @@ def gitBranchExists(branch): return proc.wait() == 0; def gitConfig(key): - return mypopen("git config %s" % key).read()[:-1] + return os.popen("git config %s" % key, "rb").read()[:-1] class Command: def __init__(self): @@ -861,7 +861,7 @@ class P4Sync(Command): if not self.silent: print "Creating/updating branch(es) in %s based on origin branch(es)" % self.refPrefix - for line in mypopen("git rev-parse --symbolic --remotes"): + for line in read_pipe_lines("git rev-parse --symbolic --remotes"): if (not line.startswith("origin/")) or line.endswith("HEAD\n"): continue -- cgit v1.2.3 From cfeb59be256bf9cd2853ed04d4af056b2f0eff31 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 28 May 2007 19:24:57 +0200 Subject: Fix typo in listExistingP4Branches that broke sync. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 0f1285b39b..794286ee8e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -846,8 +846,8 @@ class P4Sync(Command): cmdline += " --branches" for line in read_pipe_lines(cmdline): - lie = line.strip() - if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD\n"): + line = line.strip() + if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD"): continue if self.importIntoRemotes: -- cgit v1.2.3 From 2e4aef58932cdb345893103d77823a9dd6a146a6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sat, 26 May 2007 23:09:27 -0400 Subject: Allow contrib new-workdir to link into bare repositories On one particular system I like to keep a cluster of bare Git repositories and spawn new-workdirs off of them. Since the bare repositories don't have working directories associated with them they don't have a .git/ subdirectory that hosts the repository we are linking to. Using a bare repository as the backing repository for a workdir created by this script does require that the user delete core.bare from the repository's configuration file, so that Git auto-senses the bareness of a repository based on pathname information, and not based on the config file. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 9877b98508..f2a3615bbc 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -20,17 +20,19 @@ new_workdir=$2 branch=$3 # want to make sure that what is pointed to has a .git directory ... -test -d "$orig_git/.git" || die "\"$orig_git\" is not a git repository!" +git_dir=$(cd "$orig_git" 2>/dev/null && + git rev-parse --git-dir 2>/dev/null) || + die "\"$orig_git\" is not a git repository!" # don't link to a workdir -if test -L "$orig_git/.git/config" +if test -L "$git_dir/config" then die "\"$orig_git\" is a working directory only, please specify" \ "a complete repository." fi # make sure the the links use full paths -orig_git=$(cd "$orig_git"; pwd) +git_dir=$(cd "$git_dir"; pwd) # create the workdir mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!" @@ -45,13 +47,13 @@ do mkdir -p "$(dirname "$new_workdir/.git/$x")" ;; esac - ln -s "$orig_git/.git/$x" "$new_workdir/.git/$x" + ln -s "$git_dir/$x" "$new_workdir/.git/$x" done # now setup the workdir cd "$new_workdir" # copy the HEAD from the original repository as a default branch -cp "$orig_git/.git/HEAD" .git/HEAD +cp "$git_dir/HEAD" .git/HEAD # checkout the branch (either the same as HEAD from the original repository, or # the one that was asked for) git checkout -f $branch -- cgit v1.2.3 From bb6e09b27afeaae780dabfda7a07d59fc7efc4cf Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: Diverse cleanups - print commands with \n - extractDepotPathsAndChangeFromGitLog -> extractSettings, returning dict. - store keepRepoPath in [git-p4: ] line - create a main() function, so git-p4 can be pychecked - use --destination for clone destination. This simplifies logic for --keep-path Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 224 +++++++++++++++++++++++++++------------------ 1 file changed, 134 insertions(+), 90 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ad145e8595..b280e97742 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -56,7 +56,7 @@ def read_pipe_lines(c): def system(cmd): if verbose: - sys.stderr.write("executing %s" % cmd) + sys.stderr.write("executing %s\n" % cmd) if os.system(cmd) != 0: die("command failed: %s" % cmd) @@ -112,7 +112,8 @@ def currentGitBranch(): return read_pipe("git name-rev HEAD").split(" ")[1].strip() def isValidGitDir(path): - if os.path.exists(path + "/HEAD") and os.path.exists(path + "/refs") and os.path.exists(path + "/objects"): + if (os.path.exists(path + "/HEAD") + and os.path.exists(path + "/refs") and os.path.exists(path + "/objects")): return True; return False @@ -133,7 +134,7 @@ def extractLogMessageFromGitCommit(commit): logMessage += log return logMessage -def extractDepotPathsAndChangeFromGitLog(log): +def extractSettingsGitLog(log): values = {} for line in log.split("\n"): line = line.strip() @@ -151,11 +152,12 @@ def extractDepotPathsAndChangeFromGitLog(log): values[key] = val - paths = values.get("depot-path").split(',') - return paths, values.get("change") + values['depot-paths'] = values.get("depot-paths").split(',') + return values def gitBranchExists(branch): - proc = subprocess.Popen(["git", "rev-parse", branch], stderr=subprocess.PIPE, stdout=subprocess.PIPE); + proc = subprocess.Popen(["git", "rev-parse", branch], + stderr=subprocess.PIPE, stdout=subprocess.PIPE); return proc.wait() == 0; def gitConfig(key): @@ -211,7 +213,11 @@ class P4RollBack(Command): line = line.strip() ref = refPrefix + line log = extractLogMessageFromGitCommit(ref) - depotPaths, change = extractDepotPathsAndChangeFromGitLog(log) + settings = extractSettingsGitLog(log) + + depotPaths = settings['depot-paths'] + change = settings['change'] + changed = False if len(p4Cmd("changes -m 1 " + ' '.join (['%s...@%s' % (p, maxChange) @@ -220,13 +226,17 @@ class P4RollBack(Command): system("git update-ref -d %s `git rev-parse %s`" % (ref, ref)) continue - while len(change) > 0 and int(change) > maxChange: + while change and int(change) > maxChange: changed = True if self.verbose: print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange) system("git update-ref %s \"%s^\"" % (ref, ref)) log = extractLogMessageFromGitCommit(ref) - depotPaths, change = extractDepotPathsAndChangeFromGitLog(log) + settings = extractSettingsGitLog(log) + + + depotPaths = settings['depot-paths'] + change = settings['change'] if changed: print "%s rewound to %s" % (ref, change) @@ -474,10 +484,12 @@ class P4Submit(Command): return False depotPath = "" + settings = None if gitBranchExists("p4"): - [depotPaths, dummy] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit("p4")) + settings = extractSettingsGitLog(extractLogMessageFromGitCommit("p4")) if len(depotPath) == 0 and gitBranchExists("origin"): - [depotPaths, dummy] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit("origin")) + settings = extractSettingsGitLog(extractLogMessageFromGitCommit("origin")) + depotPaths = settings['depot-paths'] if len(depotPath) == 0: print "Internal error: cannot locate perforce depot path from existing branches" @@ -686,8 +698,11 @@ class P4Sync(Command): self.gitStream.write("data < 0: @@ -883,12 +898,13 @@ class P4Sync(Command): if (not line.startswith("origin/")) or line.endswith("HEAD\n"): continue - headName = line[len("origin/")] + headName = line[len("origin/"):] remoteHead = self.refPrefix + headName originHead = "origin/" + headName - [originPreviousDepotPaths, originP4Change] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit(originHead)) - if len(originPreviousDepotPaths) == 0 or len(originP4Change) == 0: + original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead)) + if (not original.has_key('depot-paths') + or not original.has_key('change')): continue update = False @@ -897,20 +913,36 @@ class P4Sync(Command): print "creating %s" % remoteHead update = True else: - [p4PreviousDepotPaths, p4Change] = extractDepotPathsAndChangeFromGitLog(extractLogMessageFromGitCommit(remoteHead)) - if len(p4Change) > 0: - if originPreviousDepotPaths == p4PreviousDepotPaths: - originP4Change = int(originP4Change) - p4Change = int(p4Change) + settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead)) + if settings.has_key('change') > 0: + if settings['depot-paths'] == original['depot-paths']: + originP4Change = int(original['change']) + p4Change = int(settings['change']) if originP4Change > p4Change: - print "%s (%s) is newer than %s (%s). Updating p4 branch from origin." % (originHead, originP4Change, remoteHead, p4Change) + print ("%s (%s) is newer than %s (%s). " + "Updating p4 branch from origin." + % (originHead, originP4Change, + remoteHead, p4Change)) update = True else: - print "Ignoring: %s was imported from %s while %s was imported from %s" % (originHead, originPreviousDepotPaths, remoteHead, p4PreviousDepotPaths) + print ("Ignoring: %s was imported from %s while " + "%s was imported from %s" + % (originHead, ','.join(original['depot-paths']), + remoteHead, ','.join(settings['depot-paths']))) if update: system("git update-ref %s %s" % (remoteHead, originHead)) + def updateOptionDict(self, d): + option_keys = {} + if self.keepRepoPath: + option_keys['keepRepoPath'] = 1 + + d["options"] = ' '.join(sorted(option_keys.keys())) + + def readOptions(self, d): + self.keepRepoPath = (d.has_key('options') + and ('keepRepoPath' in d['options'])) def run(self, args): self.depotPaths = [] @@ -942,7 +974,8 @@ class P4Sync(Command): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) - if args == []: + ### FIXME + if 1: if self.hasOrigin: self.createOrUpdateBranchesFromOrigin() self.listExistingP4GitBranches() @@ -958,19 +991,22 @@ class P4Sync(Command): p4Change = 0 for branch in self.p4BranchesInGit: logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch) - (depotPaths, change) = extractDepotPathsAndChangeFromGitLog(logMsg) + + settings = extractSettingsGitLog(logMsg) if self.verbose: print "path %s change %s" % (','.join(depotPaths), change) - if len(depotPaths) > 0 and len(change) > 0: - change = int(change) + 1 + self.readOptions(settings) + if (settings.has_key('depot-paths') + and settings.has_key ('change')): + change = int(settings['change']) + 1 p4Change = max(p4Change, change) - if len(self.previousDepotPaths) == 0: + depotPaths = sorted(settings['depot-paths']) + if self.previousDepotPaths == []: self.previousDepotPaths = depotPaths else: - ## FIXME paths = [] for (prev, cur) in zip(self.previousDepotPaths, depotPaths): for i in range(0, max(len(cur), len(prev))): @@ -982,7 +1018,7 @@ class P4Sync(Command): self.previousDepotPaths = paths if p4Change > 0: - self.depotPaths = self.previousDepotPaths + self.depotPaths = sorted(self.previousDepotPaths) self.changeRange = "@%s,#head" % p4Change self.initialParent = parseRevision(self.branch) if not self.silent and not self.detectBranches: @@ -1001,7 +1037,7 @@ class P4Sync(Command): ' '.join (args))) sys.exit(1) - self.depotPaths = args + self.depotPaths = sorted(args) self.revision = "" self.users = {} @@ -1088,7 +1124,7 @@ class P4Sync(Command): fileCnt = fileCnt + 1 details["change"] = newestRevision - + self.updateOptionDict(details) try: self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths) except IOError: @@ -1135,6 +1171,7 @@ class P4Sync(Command): cnt = 1 for change in changes: description = p4Cmd("describe %s" % change) + self.updateOptionDict(description) if not self.silent: sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) @@ -1237,7 +1274,12 @@ class P4Clone(P4Sync): def __init__(self): P4Sync.__init__(self) self.description = "Creates a new git repository and imports from Perforce into it" - self.usage = "usage: %prog [options] //depot/path[@revRange] [directory]" + self.usage = "usage: %prog [options] //depot/path[@revRange]" + self.options.append( + optparse.make_option("--destination", dest="cloneDestination", + action='store', default=None, + help="where to leave result of the clone")) + self.cloneDestination = None self.needsGit = False def run(self, args): @@ -1245,32 +1287,28 @@ class P4Clone(P4Sync): if len(args) < 1: return False - destination = "" - if self.keepRepoPath: - destination = args[-1] - args = args[:-1] - elif len(args) == 2: - destination = args[1] - elif len(args) > 2: - return False + + if self.keepRepoPath and not self.cloneDestination: + sys.stderr.write("Must specify destination for --keep-path\n") + sys.exit(1) depotPaths = args for p in depotPaths: if not p.startswith("//"): return False - if not destination: + if not self.cloneDestination: depotPath = args[0] depotDir = re.sub("(@[^@]*)$", "", depotPath) depotDir = re.sub("(#[^#]*)$", "", depotDir) depotDir = re.sub(r"\.\.\.$,", "", depotDir) depotDir = re.sub(r"/$", "", depotDir) - destination = os.path.split(depotDir)[1] + self.cloneDestination = os.path.split(depotDir)[1] - print "Importing from %s into %s" % (`depotPaths`, destination) - os.makedirs(destination) - os.chdir(destination) + print "Importing from %s into %s" % (`depotPaths`, self.cloneDestination) + os.makedirs(self.cloneDestination) + os.chdir(self.cloneDestination) system("git init") gitdir = os.getcwd() + "/.git" if not P4Sync.run(self, depotPaths): @@ -1310,54 +1348,60 @@ commands = { "rollback" : P4RollBack() } -if len(sys.argv[1:]) == 0: - printUsage(commands.keys()) - sys.exit(2) - -cmd = "" -cmdName = sys.argv[1] -try: - cmd = commands[cmdName] -except KeyError: - print "unknown command %s" % cmdName - print "" - printUsage(commands.keys()) - sys.exit(2) -options = cmd.options -cmd.gitdir = gitdir +def main(): + if len(sys.argv[1:]) == 0: + printUsage(commands.keys()) + sys.exit(2) -args = sys.argv[2:] - -if len(options) > 0: - options.append(optparse.make_option("--git-dir", dest="gitdir")) + cmd = "" + cmdName = sys.argv[1] + try: + cmd = commands[cmdName] + except KeyError: + print "unknown command %s" % cmdName + print "" + printUsage(commands.keys()) + sys.exit(2) + + options = cmd.options + cmd.gitdir = gitdir + + args = sys.argv[2:] + + if len(options) > 0: + options.append(optparse.make_option("--git-dir", dest="gitdir")) + + parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), + options, + description = cmd.description, + formatter = HelpFormatter()) + + (cmd, args) = parser.parse_args(sys.argv[2:], cmd); + global verbose + verbose = cmd.verbose + if cmd.needsGit: + gitdir = cmd.gitdir + if len(gitdir) == 0: + gitdir = ".git" + if not isValidGitDir(gitdir): + gitdir = read_pipe("git rev-parse --git-dir").strip() + if os.path.exists(gitdir): + cdup = read_pipe("git rev-parse --show-cdup").strip() + if len(cdup) > 0: + os.chdir(cdup); - parser = optparse.OptionParser(cmd.usage.replace("%prog", "%prog " + cmdName), - options, - description = cmd.description, - formatter = HelpFormatter()) + if not isValidGitDir(gitdir): + if isValidGitDir(gitdir + "/.git"): + gitdir += "/.git" + else: + die("fatal: cannot locate git repository at %s" % gitdir) - (cmd, args) = parser.parse_args(sys.argv[2:], cmd); + os.environ["GIT_DIR"] = gitdir -verbose = cmd.verbose -if cmd.needsGit: - gitdir = cmd.gitdir - if len(gitdir) == 0: - gitdir = ".git" - if not isValidGitDir(gitdir): - gitdir = read_pipe("git rev-parse --git-dir").strip() - if os.path.exists(gitdir): - cdup = read_pipe("git rev-parse --show-cdup").strip() - if len(cdup) > 0: - os.chdir(cdup); - - if not isValidGitDir(gitdir): - if isValidGitDir(gitdir + "/.git"): - gitdir += "/.git" - else: - die("fatal: cannot locate git repository at %s" % gitdir) + if not cmd.run(args): + parser.print_help() - os.environ["GIT_DIR"] = gitdir -if not cmd.run(args): - parser.print_help() +if __name__ == '__main__': + main() -- cgit v1.2.3 From b86f73782eafd1c61bb706ec5ca3d1ec548d82f5 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: remove global .gitdir Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 53 +++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 28ea53dea4..6501387657 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -14,7 +14,6 @@ import re from sets import Set; -gitdir = os.environ.get("GIT_DIR", "") verbose = False def write_pipe(c, str): @@ -469,9 +468,7 @@ class P4Submit(Command): % (fileName, fileName)) def run(self, args): - global gitdir # make gitdir absolute so we can cd out into the perforce checkout - gitdir = os.path.abspath(gitdir) os.environ["GIT_DIR"] = gitdir if len(args) == 0: @@ -510,7 +507,7 @@ class P4Submit(Command): print "No changes in working directory to submit." return True patch = read_pipe("git diff -p --binary --diff-filter=ACMRTUXB HEAD") - self.diffFile = gitdir + "/p4-git-diff" + self.diffFile = self.gitdir + "/p4-git-diff" f = open(self.diffFile, "wb") f.write(patch) f.close(); @@ -535,7 +532,7 @@ class P4Submit(Command): self.logSubstitutions[tokens[0]] = tokens[1] self.check() - self.configFile = gitdir + "/p4-git-sync.cfg" + self.configFile = self.gitdir + "/p4-git-sync.cfg" self.config = shelve.open(self.configFile, writeback=True) if self.firstTime: @@ -799,7 +796,7 @@ class P4Sync(Command): continue self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" - cache = open(gitdir + "/p4-usercache.txt", "wb") + cache = open(self.gitdir + "/p4-usercache.txt", "wb") for user in self.users.keys(): cache.write("%s\t%s\n" % (user, self.users[user])) cache.close(); @@ -809,7 +806,7 @@ class P4Sync(Command): self.users = {} self.userMapFromPerforceServer = False try: - cache = open(gitdir + "/p4-usercache.txt", "rb") + cache = open(self.gitdir + "/p4-usercache.txt", "rb") lines = cache.readlines() cache.close() for line in lines: @@ -1283,8 +1280,6 @@ class P4Clone(P4Sync): self.needsGit = False def run(self, args): - global gitdir - if len(args) < 1: return False @@ -1310,7 +1305,7 @@ class P4Clone(P4Sync): os.makedirs(self.cloneDestination) os.chdir(self.cloneDestination) system("git init") - gitdir = os.getcwd() + "/.git" + self.gitdir = os.getcwd() + "/.git" if not P4Sync.run(self, depotPaths): return False if self.branch != "master": @@ -1340,12 +1335,12 @@ def printUsage(commands): print "" commands = { - "debug" : P4Debug(), - "submit" : P4Submit(), - "sync" : P4Sync(), - "rebase" : P4Rebase(), - "clone" : P4Clone(), - "rollback" : P4RollBack() + "debug" : P4Debug, + "submit" : P4Submit, + "sync" : P4Sync, + "rebase" : P4Rebase, + "clone" : P4Clone, + "rollback" : P4RollBack } @@ -1357,7 +1352,8 @@ def main(): cmd = "" cmdName = sys.argv[1] try: - cmd = commands[cmdName] + klass = commands[cmdName] + cmd = klass() except KeyError: print "unknown command %s" % cmdName print "" @@ -1365,7 +1361,7 @@ def main(): sys.exit(2) options = cmd.options - cmd.gitdir = gitdir + cmd.gitdir = os.environ.get("GIT_DIR", None) args = sys.argv[2:] @@ -1381,23 +1377,22 @@ def main(): global verbose verbose = cmd.verbose if cmd.needsGit: - gitdir = cmd.gitdir - if len(gitdir) == 0: - gitdir = ".git" - if not isValidGitDir(gitdir): - gitdir = read_pipe("git rev-parse --git-dir").strip() - if os.path.exists(gitdir): + if cmd.gitdir == None: + cmd.gitdir = os.path.abspath(".git") + if not isValidGitDir(cmd.gitdir): + cmd.gitdir = read_pipe("git rev-parse --git-dir").strip() + if os.path.exists(cmd.gitdir): cdup = read_pipe("git rev-parse --show-cdup").strip() if len(cdup) > 0: os.chdir(cdup); - if not isValidGitDir(gitdir): - if isValidGitDir(gitdir + "/.git"): - gitdir += "/.git" + if not isValidGitDir(cmd.gitdir): + if isValidGitDir(cmd.gitdir + "/.git"): + cmd.gitdir += "/.git" else: - die("fatal: cannot locate git repository at %s" % gitdir) + die("fatal: cannot locate git repository at %s" % cmd.gitdir) - os.environ["GIT_DIR"] = gitdir + os.environ["GIT_DIR"] = cmd.gitdir if not cmd.run(args): parser.print_help() -- cgit v1.2.3 From 6a49f8e2e04317175060576d85a5d2062ebb43a4 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: Read p4 files in one batch. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 89 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 26 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6501387657..8a3c53eb8c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -61,6 +61,8 @@ def system(cmd): def p4CmdList(cmd): cmd = "p4 -G %s" % cmd + if verbose: + sys.stderr.write("Opening pipe: %s\n" % cmd) pipe = os.popen(cmd, "rb") result = [] @@ -609,9 +611,6 @@ class P4Sync(Command): if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False - def p4File(self, depotPath): - return read_pipe("p4 print -q \"%s\"" % depotPath) - def extractFilesFromCommit(self, commit): files = [] fnum = 0 @@ -673,6 +672,39 @@ class P4Sync(Command): return branches + ## Should move this out, doesn't use SELF. + def readP4Files(self, files): + specs = [(f['path'] + "#" + f['rev'], f) for f in files] + + data = read_pipe('p4 print %s' % ' '.join(['"%s"' % spec + for (spec, info) in specs])) + + idx = 0 + for j in range(0, len(specs)): + filespec, info = specs[j] + + assert idx < len(data) + if data[idx:idx + len(filespec)] != filespec: + assert False + idx = data.find ('\n', idx) + assert idx > 0 + idx += 1 + + start = idx + + end = -1 + if j < len(specs)-1: + next_spec, next_info = specs[j+1] + end = data.find(next_spec, start) + + assert end >= 0 + else: + end = len(specs) + + + info['data'] = data[start:end] + idx = end + def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] author = details["user"] @@ -681,7 +713,7 @@ class P4Sync(Command): print "commit into %s" % branch self.gitStream.write("commit %s\n" % branch) - # gitStream.write("mark :%s\n" % details["change"]) +# gitStream.write("mark :%s\n" % details["change"]) self.committedChanges.add(int(details["change"])) committer = "" if author not in self.users: @@ -707,29 +739,30 @@ class P4Sync(Command): print "parent %s" % parent self.gitStream.write("from %s\n" % parent) - for file in files: - path = file["path"] + new_files = [] + for f in files: + if [p for p in branchPrefixes if f['path'].startswith(p)]: + new_files.append (f) + else: + sys.stderr.write("Ignoring file outside of prefix: %s\n" % path) + files = new_files - if not [p for p in branchPrefixes if path.startswith(p)]: - continue - rev = file["rev"] - depotPath = path + "#" + rev - relPath = self.stripRepoPath(path, branchPrefixes) - action = file["action"] - + self.readP4Files(files) + for file in files: if file["type"] == "apple": - print "\nfile %s is a strange apple file that forks. Ignoring!" % path + print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path'] continue - if action == "delete": + relPath = self.stripRepoPath(file['path'], branchPrefixes) + if file["action"] == "delete": self.gitStream.write("D %s\n" % relPath) else: mode = 644 if file["type"].startswith("x"): mode = 755 - data = self.p4File(depotPath) + data = file['data'] if self.isWindows and file["type"].endswith("text"): data = data.replace("\r\n", "\n") @@ -971,8 +1004,9 @@ class P4Sync(Command): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) - ### FIXME - if 1: + # TODO: should always look at previous commits, + # merge with previous imports, if possible. + if args == []: if self.hasOrigin: self.createOrUpdateBranchesFromOrigin() self.listExistingP4GitBranches() @@ -1046,7 +1080,7 @@ class P4Sync(Command): self.changeRange = p[atIdx:] if self.changeRange == "@all": self.changeRange = "" - elif self.changeRange.find(",") == -1: + elif ',' not in self.changeRange: self.revision = self.changeRange self.changeRange = "" p = p[0:atIdx] @@ -1279,6 +1313,15 @@ class P4Clone(P4Sync): self.cloneDestination = None self.needsGit = False + def defaultDestination(self, args): + ## TODO: use common prefix of args? + depotPath = args[0] + depotDir = re.sub("(@[^@]*)$", "", depotPath) + depotDir = re.sub("(#[^#]*)$", "", depotDir) + depotDir = re.sub(r"\.\.\.$,", "", depotDir) + depotDir = re.sub(r"/$", "", depotDir) + return os.path.split(depotDir)[1] + def run(self, args): if len(args) < 1: return False @@ -1293,13 +1336,7 @@ class P4Clone(P4Sync): return False if not self.cloneDestination: - depotPath = args[0] - depotDir = re.sub("(@[^@]*)$", "", depotPath) - depotDir = re.sub("(#[^#]*)$", "", depotDir) - depotDir = re.sub(r"\.\.\.$,", "", depotDir) - depotDir = re.sub(r"/$", "", depotDir) - - self.cloneDestination = os.path.split(depotDir)[1] + self.cloneDestination = self.defaultDestination() print "Importing from %s into %s" % (`depotPaths`, self.cloneDestination) os.makedirs(self.cloneDestination) -- cgit v1.2.3 From 9320da8dd492c13f1d32b7fde8c9e60bdbd10217 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: Thinko, fix buglet. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 8a3c53eb8c..f1f562fae4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -699,11 +699,11 @@ class P4Sync(Command): assert end >= 0 else: - end = len(specs) - + end = len(data) info['data'] = data[start:end] idx = end + assert idx == len(data) def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] -- cgit v1.2.3 From 183b8ef89be041cd50803427cfc46fe1afed17bb Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: store p4 user cache in home directory. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f1f562fae4..bd1afb2964 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -819,6 +819,9 @@ class P4Sync(Command): print ("Tag %s does not match with change %s: file count is different." % (labelDetails["label"], change)) + def getUserCacheFilename(self): + return os.environ["HOME"] + "/.gitp4-usercache.txt") + def getUserMapFromPerforceServer(self): if self.userMapFromPerforceServer: return @@ -829,17 +832,19 @@ class P4Sync(Command): continue self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">" - cache = open(self.gitdir + "/p4-usercache.txt", "wb") - for user in self.users.keys(): - cache.write("%s\t%s\n" % (user, self.users[user])) - cache.close(); + + s = '' + for (key, val) in self.users.items(): + s += "%s\t%s\n" % (key, val) + + open(self.getUserCacheFilename(), "wb").write(s) self.userMapFromPerforceServer = True def loadUserMapFromCache(self): self.users = {} self.userMapFromPerforceServer = False try: - cache = open(self.gitdir + "/p4-usercache.txt", "rb") + cache = open(self.getUserCacheFilename(), "rb") lines = cache.readlines() cache.close() for line in lines: -- cgit v1.2.3 From a3287be5bc40c1aa036eb5422db5a6a087d1736e Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: thinko. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bd1afb2964..01efd92809 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -820,7 +820,7 @@ class P4Sync(Command): % (labelDetails["label"], change)) def getUserCacheFilename(self): - return os.environ["HOME"] + "/.gitp4-usercache.txt") + return os.environ["HOME"] + "/.gitp4-usercache.txt" def getUserMapFromPerforceServer(self): if self.userMapFromPerforceServer: -- cgit v1.2.3 From 96e07dd23c570fe5e65e619c0f1e3c87be2a8352 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: read files before creating the commit. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 01efd92809..d1989e6134 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -697,6 +697,9 @@ class P4Sync(Command): next_spec, next_info = specs[j+1] end = data.find(next_spec, start) + if end < 0: + print spec, next_spec + assert end >= 0 else: end = len(data) @@ -712,6 +715,20 @@ class P4Sync(Command): if self.verbose: print "commit into %s" % branch + # start with reading files; if that fails, we should not + # create a commit. + new_files = [] + for f in files: + if [p for p in branchPrefixes if f['path'].startswith(p)]: + new_files.append (f) + else: + sys.stderr.write("Ignoring file outside of prefix: %s\n" % path) + files = new_files + self.readP4Files(files) + + + + self.gitStream.write("commit %s\n" % branch) # gitStream.write("mark :%s\n" % details["change"]) self.committedChanges.add(int(details["change"])) @@ -739,16 +756,6 @@ class P4Sync(Command): print "parent %s" % parent self.gitStream.write("from %s\n" % parent) - - new_files = [] - for f in files: - if [p for p in branchPrefixes if f['path'].startswith(p)]: - new_files.append (f) - else: - sys.stderr.write("Ignoring file outside of prefix: %s\n" % path) - files = new_files - - self.readP4Files(files) for file in files: if file["type"] == "apple": print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path'] @@ -1030,9 +1037,6 @@ class P4Sync(Command): settings = extractSettingsGitLog(logMsg) - if self.verbose: - print "path %s change %s" % (','.join(depotPaths), change) - self.readOptions(settings) if (settings.has_key('depot-paths') and settings.has_key ('change')): @@ -1145,6 +1149,9 @@ class P4Sync(Command): + ' '.join(["%s...%s" % (p, self.revision) for p in self.depotPaths])): + + if not info.has_key("change"): + print info change = int(info["change"]) if change > newestRevision: newestRevision = change @@ -1154,7 +1161,7 @@ class P4Sync(Command): #fileCnt = fileCnt + 1 continue - for prop in [ "depotFile", "rev", "action", "type" ]: + for prop in ["depotFile", "rev", "action", "type" ]: details["%s%s" % (prop, fileCnt)] = info[prop] fileCnt = fileCnt + 1 -- cgit v1.2.3 From 982bb8a30376d0024a1794426e6e2291a7a21294 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: don't p4 print deleted files. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d1989e6134..63d7a4c995 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -674,17 +674,18 @@ class P4Sync(Command): ## Should move this out, doesn't use SELF. def readP4Files(self, files): - specs = [(f['path'] + "#" + f['rev'], f) for f in files] + specs = [(f['path'] + "#" + f['rev'], f) for f in files + if f['action'] != 'delete'] - data = read_pipe('p4 print %s' % ' '.join(['"%s"' % spec - for (spec, info) in specs])) + data = read_pipe('p4 print %s' % ' '.join(['"%s"' % path + for (path, info) in specs])) idx = 0 for j in range(0, len(specs)): - filespec, info = specs[j] + (pathrev, info) = specs[j] assert idx < len(data) - if data[idx:idx + len(filespec)] != filespec: + if data[idx:idx + len(pathrev)] != pathrev: assert False idx = data.find ('\n', idx) assert idx > 0 @@ -694,11 +695,15 @@ class P4Sync(Command): end = -1 if j < len(specs)-1: - next_spec, next_info = specs[j+1] - end = data.find(next_spec, start) + (next_pathrev, next_info) = specs[j+1] + end = data.find(next_pathrev, start) if end < 0: - print spec, next_spec + print 'j' + print 'PATHREV', pathrev, specs[j] + print 'nextpathrev', next_pathrev, specs[j+1] + print 'start', start, len(data) + print 'end', end assert end >= 0 else: -- cgit v1.2.3 From f2eda79f6967363f9377ef3b137a35d0c86aca2c Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: only run p4 print if necessary Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 63d7a4c995..76bbe3fdbf 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -677,6 +677,9 @@ class P4Sync(Command): specs = [(f['path'] + "#" + f['rev'], f) for f in files if f['action'] != 'delete'] + if not specs: + return + data = read_pipe('p4 print %s' % ' '.join(['"%s"' % path for (path, info) in specs])) -- cgit v1.2.3 From d2c6dd30eff648aef30d003ef327ab9415f86db0 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: use p4CmdList() to get file contents in Python dicts. This is more robust. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 56 +++++++++++++++++----------------------------- 1 file changed, 21 insertions(+), 35 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 76bbe3fdbf..1d799708f6 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -580,7 +580,8 @@ class P4Sync(Command): optparse.make_option("--silent", dest="silent", action="store_true"), optparse.make_option("--detect-labels", dest="detectLabels", action="store_true"), optparse.make_option("--verbose", dest="verbose", action="store_true"), - optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false"), + optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false", + help="Import into refs/heads/ , not refs/remotes"), optparse.make_option("--max-changes", dest="maxChanges"), optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true') ] @@ -680,41 +681,22 @@ class P4Sync(Command): if not specs: return - data = read_pipe('p4 print %s' % ' '.join(['"%s"' % path - for (path, info) in specs])) + filedata = p4CmdList('print %s' % ' '.join(['"%s"' % path + for (path, info) in specs])) - idx = 0 - for j in range(0, len(specs)): - (pathrev, info) = specs[j] + j = 0; + contents = {} + while filedata[j:]: + stat = filedata[j] + text = filedata[j+1] + j += 2 - assert idx < len(data) - if data[idx:idx + len(pathrev)] != pathrev: - assert False - idx = data.find ('\n', idx) - assert idx > 0 - idx += 1 + assert stat['code'] == 'stat' and text['code'] == 'text' + contents[stat['depotFile']] = text['data'] - start = idx - - end = -1 - if j < len(specs)-1: - (next_pathrev, next_info) = specs[j+1] - end = data.find(next_pathrev, start) - - if end < 0: - print 'j' - print 'PATHREV', pathrev, specs[j] - print 'nextpathrev', next_pathrev, specs[j+1] - print 'start', start, len(data) - print 'end', end - - assert end >= 0 - else: - end = len(data) - - info['data'] = data[start:end] - idx = end - assert idx == len(data) + for f in files: + assert not f.has_key('data') + f['data'] = contents[f['path']] def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] @@ -1158,8 +1140,12 @@ class P4Sync(Command): % (p, self.revision) for p in self.depotPaths])): - if not info.has_key("change"): - print info + if info['code'] == 'error': + sys.stderr.write("p4 returned an error: %s\n" + % info['data']) + sys.exit(1) + + change = int(info["change"]) if change > newestRevision: newestRevision = change -- cgit v1.2.3 From 86dff6b6762149d5f3d4b44bb57f58a8399a33ee Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: Cleanups & import into p4/master for local import - import into master/local if --import-local is set - use Die() for exiting - if --verbose is set, raise Exception() - use joined strings iso. `list` for progress printing Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 54 +++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 22 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1d799708f6..cc0b7013da 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -16,40 +16,44 @@ from sets import Set; verbose = False +def die(msg): + if verbose: + raise Exception(msg) + else: + sys.stderr.write(msg + "\n") + sys.exit(1) + def write_pipe(c, str): if verbose: - sys.stderr.write('writing pipe: %s\n' % c) + sys.stderr.write('Writing pipe: %s\n' % c) pipe = os.popen(c, 'w') val = pipe.write(str) if pipe.close(): - sys.stderr.write('Command failed: %s' % c) - sys.exit(1) + die('Command failed: %s' % c) return val def read_pipe(c, ignore_error=False): if verbose: - sys.stderr.write('reading pipe: %s\n' % c) + sys.stderr.write('Reading pipe: %s\n' % c) pipe = os.popen(c, 'rb') val = pipe.read() if pipe.close() and not ignore_error: - sys.stderr.write('Command failed: %s\n' % c) - sys.exit(1) + die('Command failed: %s' % c) return val def read_pipe_lines(c): if verbose: - sys.stderr.write('reading pipe: %s\n' % c) + sys.stderr.write('Reading pipe: %s\n' % c) ## todo: check return status pipe = os.popen(c, 'rb') val = pipe.readlines() if pipe.close(): - sys.stderr.write('Command failed: %s\n' % c) - sys.exit(1) + die('Command failed: %s' % c) return val @@ -105,10 +109,6 @@ def p4Where(depotPath): clientPath = clientPath[:-3] return clientPath -def die(msg): - sys.stderr.write(msg + "\n") - sys.exit(1) - def currentGitBranch(): return read_pipe("git name-rev HEAD").split(" ")[1].strip() @@ -583,7 +583,8 @@ class P4Sync(Command): optparse.make_option("--import-local", dest="importIntoRemotes", action="store_false", help="Import into refs/heads/ , not refs/remotes"), optparse.make_option("--max-changes", dest="maxChanges"), - optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true') + optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true', + help="Keep entire BRANCH/DIR/SUBDIR prefix during import") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -876,10 +877,14 @@ class P4Sync(Command): if self.verbose: print "Label changes: %s" % self.labels.keys() + def guessProjectName(self): + for p in self.depotPaths: + return p [p.strip().rfind("/") + 1:] + def getBranchMapping(self): ## FIXME - what's a P4 projectName ? - self.projectName = self.depotPath[self.depotPath.strip().rfind("/") + 1:] + self.projectName = self.guessProjectName() for info in p4CmdList("branches"): details = p4Cmd("branch -o %s" % info["branch"]) @@ -911,9 +916,11 @@ class P4Sync(Command): for line in read_pipe_lines(cmdline): line = line.strip() - if self.importIntoRemotes and ((not line.startswith("p4/")) or line == "p4/HEAD"): - continue + ## only import to p4/ + if not line.startswith('p4/'): + continue + branch = line if self.importIntoRemotes: # strip off p4 branch = re.sub ("^p4/", "", line) @@ -923,7 +930,8 @@ class P4Sync(Command): def createOrUpdateBranchesFromOrigin(self): if not self.silent: - print "Creating/updating branch(es) in %s based on origin branch(es)" % self.refPrefix + print ("Creating/updating branch(es) in %s based on origin branch(es)" + % self.refPrefix) for line in read_pipe_lines("git rev-parse --symbolic --remotes"): line = line.strip() @@ -998,7 +1006,7 @@ class P4Sync(Command): system("git fetch origin") if len(self.branch) == 0: - self.branch = self.refPrefix + "master" + self.branch = self.refPrefix + "p4/master" if gitBranchExists("refs/heads/p4") and self.importIntoRemotes: system("git update-ref %s refs/heads/p4" % self.branch) system("git branch -D p4"); @@ -1023,6 +1031,7 @@ class P4Sync(Command): p4Change = 0 for branch in self.p4BranchesInGit: + print self.p4BranchesInGit logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch) settings = extractSettingsGitLog(logMsg) @@ -1125,7 +1134,7 @@ class P4Sync(Command): self.gitStream = importProcess.stdin self.gitError = importProcess.stderr - if len(self.revision) > 0: + if self.revision: print "Doing initial import of %s from revision %s" % (' '.join(self.depotPaths), self.revision) details = { "user" : "git perforce import user", "time" : int(time.time()) } @@ -1183,7 +1192,7 @@ class P4Sync(Command): changes.sort() else: if self.verbose: - print "Getting p4 changes for %s...%s" % (`self.depotPaths`, + print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths), self.changeRange) assert self.depotPaths output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange) @@ -1344,7 +1353,7 @@ class P4Clone(P4Sync): if not self.cloneDestination: self.cloneDestination = self.defaultDestination() - print "Importing from %s into %s" % (`depotPaths`, self.cloneDestination) + print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination) os.makedirs(self.cloneDestination) os.chdir(self.cloneDestination) system("git init") @@ -1357,6 +1366,7 @@ class P4Clone(P4Sync): system("git checkout -f") else: print "Could not detect main branch. No checkout/master branch created." + return True class HelpFormatter(optparse.IndentedHelpFormatter): -- cgit v1.2.3 From b17f88b5445210c2223b0642917c43581c767761 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: remove debug print Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 1 - 1 file changed, 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index cc0b7013da..5dae1f19ae 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1031,7 +1031,6 @@ class P4Sync(Command): p4Change = 0 for branch in self.p4BranchesInGit: - print self.p4BranchesInGit logMsg = extractLogMessageFromGitCommit(self.refPrefix + branch) settings = extractSettingsGitLog(logMsg) -- cgit v1.2.3 From b1ce94472684957cd5aba759f495a889f154a9a2 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: thinko: really ignore deleted files. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 5dae1f19ae..294c2ecfca 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -173,13 +173,18 @@ class P4Debug(Command): def __init__(self): Command.__init__(self) self.options = [ - optparse.make_option("--verbose", dest="verbose", action="store_true"), + optparse.make_option("--verbose", dest="verbose", action="store_true", + default=False), ] self.description = "A tool to debug the output of p4 -G." self.needsGit = False + self.verbose = False def run(self, args): + j = 0 for output in p4CmdList(" ".join(args)): + print 'Element: %d' % j + j += 1 print output return True @@ -676,24 +681,27 @@ class P4Sync(Command): ## Should move this out, doesn't use SELF. def readP4Files(self, files): - specs = [(f['path'] + "#" + f['rev'], f) for f in files + files = [f for f in files if f['action'] != 'delete'] - if not specs: + if not files: return - filedata = p4CmdList('print %s' % ' '.join(['"%s"' % path - for (path, info) in specs])) + filedata = p4CmdList('print %s' % ' '.join(['"%s#%s"' % (f['path'], + f['rev']) + for f in files])) j = 0; contents = {} - while filedata[j:]: + while j < len(filedata): stat = filedata[j] - text = filedata[j+1] - j += 2 + j += 1 + text = '' + while j < len(filedata) and filedata[j]['code'] == 'text': + text += filedata[j]['data'] + j += 1 - assert stat['code'] == 'stat' and text['code'] == 'text' - contents[stat['depotFile']] = text['data'] + contents[stat['depotFile']] = text for f in files: assert not f.has_key('data') -- cgit v1.2.3 From 7530a40ce2006082580865f4db6d32b956ca8dc0 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: look for 'text' and 'binary' files. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 294c2ecfca..e955ad4f44 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -697,7 +697,8 @@ class P4Sync(Command): stat = filedata[j] j += 1 text = '' - while j < len(filedata) and filedata[j]['code'] == 'text': + while j < len(filedata) and filedata[j]['code'] in ('text', + 'binary'): text += filedata[j]['data'] j += 1 @@ -773,7 +774,7 @@ class P4Sync(Command): if self.isWindows and file["type"].endswith("text"): data = data.replace("\r\n", "\n") - self.gitStream.write("M %s inline %s\n" % (mode, relPath)) + self.gitStream.write("M %d inline %s\n" % (mode, relPath)) self.gitStream.write("data %s\n" % len(data)) self.gitStream.write(data) self.gitStream.write("\n") -- cgit v1.2.3 From a6080a0a44d5ead84db3dabbbc80e82df838533d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 7 Jun 2007 00:04:01 -0700 Subject: War on whitespace This uses "git-apply --whitespace=strip" to fix whitespace errors that have crept in to our source files over time. There are a few files that need to have trailing whitespaces (most notably, test vectors). The results still passes the test, and build result in Documentation/ area is unchanged. Signed-off-by: Junio C Hamano --- contrib/README | 1 - contrib/blameview/README | 1 - contrib/gitview/gitview | 2 -- contrib/hooks/post-receive-email | 2 +- contrib/remotes2config.sh | 2 -- 5 files changed, 1 insertion(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/README b/contrib/README index e1c0a01ff3..05f291c1f1 100644 --- a/contrib/README +++ b/contrib/README @@ -41,4 +41,3 @@ submit a patch to create a subdirectory of contrib/ and put your stuff there. -jc - diff --git a/contrib/blameview/README b/contrib/blameview/README index 50a6f67fd6..fada5ce909 100644 --- a/contrib/blameview/README +++ b/contrib/blameview/README @@ -7,4 +7,3 @@ To: Linus Torvalds Cc: git@vger.kernel.org Date: Sat, 27 Jan 2007 18:52:38 -0500 Message-ID: <20070127235238.GA28706@coredump.intra.peff.net> - diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 2d80e2bad2..3dc1ef50c7 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -1277,5 +1277,3 @@ if __name__ == "__main__": view = GitView( without_diff != 1) view.run(sys.argv[without_diff:]) - - diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index d1bef9125b..c589a39a0c 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -199,7 +199,7 @@ generate_email_footer() hooks/post-receive - -- + -- $projectdesc EOF } diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh index dc09eae972..0c8b954490 100644 --- a/contrib/remotes2config.sh +++ b/contrib/remotes2config.sh @@ -31,5 +31,3 @@ if [ -d "$GIT_DIR"/remotes ]; then esac done fi - - -- cgit v1.2.3 From 845b42cb6c1c1e80c4283234ea2162cae809bd50 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 09:19:34 +0200 Subject: Fix support for "depot-path" in older git-p4 imports Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e955ad4f44..e576f2dd9e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -153,7 +153,10 @@ def extractSettingsGitLog(log): values[key] = val - values['depot-paths'] = values.get("depot-paths").split(',') + paths = values.get("depot-paths") + if not paths: + paths = values.get("depot-path") + values['depot-paths'] = paths.split(',') return values def gitBranchExists(branch): -- cgit v1.2.3 From 583e170706f5d69770d0de220286f8f0f73667f3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 09:37:13 +0200 Subject: Fix common path "calculation" from logs of multiple branches. Need to use min instead of max for prev/cur to avoid out-of-bounds string access. Also treat "i" as index of the last match instead of a length because in case of a complete match of the two strings i was off by one. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e576f2dd9e..ba34f0a61f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1059,11 +1059,12 @@ class P4Sync(Command): else: paths = [] for (prev, cur) in zip(self.previousDepotPaths, depotPaths): - for i in range(0, max(len(cur), len(prev))): + for i in range(0, min(len(cur), len(prev))): if cur[i] <> prev[i]: + i = i - 1 break - paths.append (cur[:i]) + paths.append (cur[:i + 1]) self.previousDepotPaths = paths -- cgit v1.2.3 From 330f53b8d679ece8363a73ed6f6e211d99cd5fdf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 09:39:51 +0200 Subject: Don't attempt to set the initialParent on multi-branch imports (useless). At some point the code paths should be unified, but for now I need a working git-p4 :) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ba34f0a61f..ff737d762c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1071,7 +1071,8 @@ class P4Sync(Command): if p4Change > 0: self.depotPaths = sorted(self.previousDepotPaths) self.changeRange = "@%s,#head" % p4Change - self.initialParent = parseRevision(self.branch) + if not self.detectBranches: + self.initialParent = parseRevision(self.branch) if not self.silent and not self.detectBranches: print "Performing incremental import into %s git branch" % self.branch -- cgit v1.2.3 From 6509e19cd1f6b5620d339a2be35b8a160ab30794 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 09:41:53 +0200 Subject: Hack to make the multi-branch import work again with self.depotPaths now that self.depotPath is gone Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ff737d762c..ececc44518 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -909,9 +909,10 @@ class P4Sync(Command): continue source = paths[0] destination = paths[1] - if source.startswith(self.depotPath) and destination.startswith(self.depotPath): - source = source[len(self.depotPath):-4] - destination = destination[len(self.depotPath):-4] + ## HACK + if source.startswith(self.depotPaths[0]) and destination.startswith(self.depotPaths[0]): + source = source[len(self.depotPaths[0]):-4] + destination = destination[len(self.depotPaths[0]):-4] if destination not in self.knownBranches: self.knownBranches[destination] = source if source not in self.knownBranches: -- cgit v1.2.3 From 68c42153060cab9ea6c16256febcf736a2a710d9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 12:51:03 +0200 Subject: Fix git-p4 rebase Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ececc44518..50d92c0f42 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1320,6 +1320,7 @@ class P4Rebase(Command): self.options = [ ] self.description = ("Fetches the latest revision from perforce and " + "rebases the current work (branch) against it") + self.verbose = False def run(self, args): sync = P4Sync() -- cgit v1.2.3 From b0d10df77a1130bdf3b4f59fdc18312432b90823 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 13:09:14 +0200 Subject: Fix git-p4 submit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 50d92c0f42..8be0afe828 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -276,6 +276,7 @@ class P4Submit(Command): self.origin = "" self.directSubmit = False self.trustMeLikeAFool = False + self.verbose = False self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" @@ -478,9 +479,6 @@ class P4Submit(Command): % (fileName, fileName)) def run(self, args): - # make gitdir absolute so we can cd out into the perforce checkout - os.environ["GIT_DIR"] = gitdir - if len(args) == 0: self.master = currentGitBranch() if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master): -- cgit v1.2.3 From a52d5c7bc027248fca472a402882586a9eaf59bf Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 13:10:20 +0200 Subject: Fix depot-path determination for git-p4 submit Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 8be0afe828..8b00e350ec 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -494,7 +494,7 @@ class P4Submit(Command): settings = extractSettingsGitLog(extractLogMessageFromGitCommit("p4")) if len(depotPath) == 0 and gitBranchExists("origin"): settings = extractSettingsGitLog(extractLogMessageFromGitCommit("origin")) - depotPaths = settings['depot-paths'] + depotPath = settings['depot-paths'][0] if len(depotPath) == 0: print "Internal error: cannot locate perforce depot path from existing branches" -- cgit v1.2.3 From f7baba8b092bdbc31196de1095c7779b633e28b1 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Thu, 7 Jun 2007 14:07:01 +0200 Subject: Ensure that the commit message is Windows formated (CRLF) before invoking the editor. (The default editor on Windows (Notepad) doesn't handle Unix line endings) Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 8b00e350ec..fc4e7d26f0 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -277,6 +277,7 @@ class P4Submit(Command): self.directSubmit = False self.trustMeLikeAFool = False self.verbose = False + self.isWindows = (platform.system() == "Windows") self.logSubstitutions = {} self.logSubstitutions[""] = "%log%" @@ -398,6 +399,8 @@ class P4Submit(Command): if not self.directSubmit: logMessage = extractLogMessageFromGitCommit(id) logMessage = logMessage.replace("\n", "\n\t") + if self.isWindows: + logMessage = logMessage.replace("\n", "\r\n") logMessage = logMessage.strip() template = read_pipe("p4 change -o") @@ -444,6 +447,8 @@ class P4Submit(Command): tmpFile.close() os.remove(fileName) submitTemplate = message[:message.index(separatorLine)] + if self.isWindows: + submitTemplate = submitTemplate.replace("\r\n", "\n") if response == "y" or response == "yes": if self.dryRun: -- cgit v1.2.3 From 98ad4faf95c1e98d6f1aaccda6d96a00a3cddeed Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Thu, 7 Jun 2007 15:08:33 +0200 Subject: Fix git-p4 clone (defaultDestination) Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index fc4e7d26f0..89581eacaa 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1369,7 +1369,7 @@ class P4Clone(P4Sync): return False if not self.cloneDestination: - self.cloneDestination = self.defaultDestination() + self.cloneDestination = self.defaultDestination(args) print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination) os.makedirs(self.cloneDestination) -- cgit v1.2.3 From db775559c2d866de895cc36532ddff6b1ebbeda9 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Thu, 7 Jun 2007 15:13:59 +0200 Subject: Fix single branch import into remotes Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 89581eacaa..ad023f203c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1014,7 +1014,7 @@ class P4Sync(Command): if self.importIntoRemotes: self.refPrefix = "refs/remotes/p4/" else: - self.refPrefix = "refs/heads/" + self.refPrefix = "refs/heads/p4/" if self.syncWithOrigin and self.hasOrigin: if not self.silent: @@ -1022,7 +1022,7 @@ class P4Sync(Command): system("git fetch origin") if len(self.branch) == 0: - self.branch = self.refPrefix + "p4/master" + self.branch = self.refPrefix + "master" if gitBranchExists("refs/heads/p4") and self.importIntoRemotes: system("git update-ref %s refs/heads/p4" % self.branch) system("git branch -D p4"); -- cgit v1.2.3 From c4b33253c221d928f3edde71123a44765495b31a Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Thu, 7 Jun 2007 15:28:04 +0200 Subject: Exclude the HEAD symbolic ref from the list of known branches Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ad023f203c..965b391cda 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -934,7 +934,7 @@ class P4Sync(Command): line = line.strip() ## only import to p4/ - if not line.startswith('p4/'): + if not line.startswith('p4/') or line == "p4/HEAD": continue branch = line if self.importIntoRemotes: -- cgit v1.2.3 From 5e100b5cd7210f8054fd3464872c0366abc3c1dc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 21:12:25 +0200 Subject: Make clone behave like git clone by default again. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 965b391cda..3fe7ae77a8 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1364,6 +1364,11 @@ class P4Clone(P4Sync): sys.exit(1) depotPaths = args + + if not self.cloneDestination and len(depotPaths) > 1: + self.cloneDestination = depotPaths[-1] + depotPaths = depotPaths[:-1] + for p in depotPaths: if not p.startswith("//"): return False -- cgit v1.2.3 From a3fdd57901bfe1014c4a48e13815d80f1f0e8577 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 7 Jun 2007 22:54:32 +0200 Subject: Make git-p4 submit detect the correct reference (origin) branch when working with multi-branch imports. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3fe7ae77a8..efec0be32c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -156,7 +156,8 @@ def extractSettingsGitLog(log): paths = values.get("depot-paths") if not paths: paths = values.get("depot-path") - values['depot-paths'] = paths.split(',') + if paths: + values['depot-paths'] = paths.split(',') return values def gitBranchExists(branch): @@ -494,12 +495,27 @@ class P4Submit(Command): return False depotPath = "" - settings = None - if gitBranchExists("p4"): - settings = extractSettingsGitLog(extractLogMessageFromGitCommit("p4")) - if len(depotPath) == 0 and gitBranchExists("origin"): - settings = extractSettingsGitLog(extractLogMessageFromGitCommit("origin")) - depotPath = settings['depot-paths'][0] + parent = 0 + while parent < 65535: + commit = "HEAD~%s" % parent + log = extractLogMessageFromGitCommit(commit) + settings = extractSettingsGitLog(log) + if not settings.has_key("depot-paths"): + parent = parent + 1 + continue + + depotPath = settings['depot-paths'][0] + + if len(self.origin) == 0: + names = read_pipe_lines("git name-rev '--refs=refs/remotes/p4/*' '%s'" % commit) + if len(names) > 0: + # strip away the beginning of 'HEAD~42 refs/remotes/p4/foo' + self.origin = names[0].strip()[len(commit) + 1:] + + break + + if self.verbose: + print "Origin branch is " + self.origin if len(depotPath) == 0: print "Internal error: cannot locate perforce depot path from existing branches" @@ -530,12 +546,6 @@ class P4Submit(Command): if response == "y" or response == "yes": system("p4 sync ...") - if len(self.origin) == 0: - if gitBranchExists("p4"): - self.origin = "p4" - else: - self.origin = "origin" - if self.reset: self.firstTime = True @@ -969,7 +979,7 @@ class P4Sync(Command): print "creating %s" % remoteHead update = True else: - settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead)) + settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead)) if settings.has_key('change') > 0: if settings['depot-paths'] == original['depot-paths']: originP4Change = int(original['change']) -- cgit v1.2.3 From df450923a2a08c50976f2d241a1c4992cf03b3a7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 8 Jun 2007 08:49:22 +0200 Subject: Only get the expensive branch mapping from the p4 server when not syncing with the help of an origin remote (which we instead then use to get new branches from). Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index efec0be32c..36fe69a795 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -907,10 +907,6 @@ class P4Sync(Command): return p [p.strip().rfind("/") + 1:] def getBranchMapping(self): - - ## FIXME - what's a P4 projectName ? - self.projectName = self.guessProjectName() - for info in p4CmdList("branches"): details = p4Cmd("branch -o %s" % info["branch"]) viewIdx = 0 @@ -1141,7 +1137,11 @@ class P4Sync(Command): self.getLabels(); if self.detectBranches: - self.getBranchMapping(); + ## FIXME - what's a P4 projectName ? + self.projectName = self.guessProjectName() + + if not self.hasOrigin: + self.getBranchMapping(); if self.verbose: print "p4-git branches: %s" % self.p4BranchesInGit print "initial parents: %s" % self.initialParents -- cgit v1.2.3 From 709b148a907e68bfb57808de8f65b186cc9a5e21 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 30 May 2007 14:47:08 +1000 Subject: gitview: Use new-style classes This changes the Commit class to use new-style class, which has been available since Python 2.2 (Dec 2001). This is a necessary step in order to use __slots__[] declaration, so that we can reduce the memory footprint in the next patch. Signed-off-by: Michael Ellerman Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 3dc1ef50c7..7e1d68d167 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -259,7 +259,7 @@ class CellRendererGraph(gtk.GenericCellRenderer): self.set_colour(ctx, colour, 0.0, 0.5) ctx.show_text(name) -class Commit: +class Commit(object): """ This represent a commit object obtained after parsing the git-rev-list output """ @@ -339,7 +339,7 @@ class Commit: fp.close() return diff -class AnnotateWindow: +class AnnotateWindow(object): """Annotate window. This object represents and manages a single window containing the annotate information of the file @@ -519,7 +519,7 @@ class AnnotateWindow: self.io_watch_tag = gobject.io_add_watch(fp, gobject.IO_IN, self.data_ready) -class DiffWindow: +class DiffWindow(object): """Diff window. This object represents and manages a single window containing the differences between two revisions on a branch. @@ -674,7 +674,7 @@ class DiffWindow: fp.close() dialog.destroy() -class GitView: +class GitView(object): """ This is the main class """ version = "0.9" -- cgit v1.2.3 From 225696af2ceaa2e06345954003eda742a76c4e0a Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 30 May 2007 14:47:09 +1000 Subject: gitview: Define __slots__ for Commit Define __slots__ for the Commit class. This reserves space in each Commit object for only the defined variables. On my system this reduces heap usage when viewing a kernel repo by 12% ~= 55868 KB. Signed-off-by: Michael Ellerman Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 7e1d68d167..098cb01353 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -263,6 +263,9 @@ class Commit(object): """ This represent a commit object obtained after parsing the git-rev-list output """ + __slots__ = ['children_sha1', 'message', 'author', 'date', 'committer', + 'commit_date', 'commit_sha1', 'parent_sha1'] + children_sha1 = {} def __init__(self, commit_lines): -- cgit v1.2.3 From 1b9a46849a45f2b0f58d6286957316f720a301b6 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: print error message when p4 print fails (eg. due to permission problems) Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e955ad4f44..35c24c93e3 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -702,6 +702,11 @@ class P4Sync(Command): text += filedata[j]['data'] j += 1 + + if not stat.has_key('depotFile'): + sys.stderr.write("p4 print fails with: %s\n" % repr(stat)) + continue + contents[stat['depotFile']] = text for f in files: -- cgit v1.2.3 From 5265bfcb06f420841e6304b278b552a4654a4ba0 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Wed, 23 May 2007 18:49:35 -0300 Subject: also strip p4/ from local imports. Signed-off-by: Han-Wen Nienhuys --- contrib/fast-import/git-p4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 28e37fa6ab..88ea12cb37 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -948,9 +948,9 @@ class P4Sync(Command): if not line.startswith('p4/') or line == "p4/HEAD": continue branch = line - if self.importIntoRemotes: - # strip off p4 - branch = re.sub ("^p4/", "", line) + + # strip off p4 + branch = re.sub ("^p4/", "", line) self.p4BranchesInGit.append(branch) self.initialParents[self.refPrefix + branch] = parseRevision(line) -- cgit v1.2.3 From 7aded26ce870ba2998e276604fc3c18dad6133bd Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 10 Jun 2007 00:22:30 +0200 Subject: Fixed the check to make sure to exclude the HEAD symbolic refs when updating the remotes/p4 branches from origin. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 88ea12cb37..d03ba0b6ad 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -962,7 +962,7 @@ class P4Sync(Command): for line in read_pipe_lines("git rev-parse --symbolic --remotes"): line = line.strip() - if (not line.startswith("origin/")) or line.endswith("HEAD\n"): + if (not line.startswith("origin/")) or line.endswith("HEAD"): continue headName = line[len("origin/"):] -- cgit v1.2.3 From cae7b732d859b06a4f560271099891f15c5700f6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 10 Jun 2007 10:57:40 +0200 Subject: Fix updating/creating remotes/p4/* heads from origin/p4/* Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d03ba0b6ad..9d52eada42 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -960,14 +960,16 @@ class P4Sync(Command): print ("Creating/updating branch(es) in %s based on origin branch(es)" % self.refPrefix) + originPrefix = "origin/p4/" + for line in read_pipe_lines("git rev-parse --symbolic --remotes"): line = line.strip() - if (not line.startswith("origin/")) or line.endswith("HEAD"): + if (not line.startswith(originPrefix)) or line.endswith("HEAD"): continue - headName = line[len("origin/"):] + headName = line[len(originPrefix):] remoteHead = self.refPrefix + headName - originHead = "origin/" + headName + originHead = line original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead)) if (not original.has_key('depot-paths') @@ -1020,7 +1022,7 @@ class P4Sync(Command): # map from branch depot path to parent branch self.knownBranches = {} self.initialParents = {} - self.hasOrigin = gitBranchExists("origin") + self.hasOrigin = gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") if self.importIntoRemotes: self.refPrefix = "refs/remotes/p4/" -- cgit v1.2.3 From 6e5295c4d3e4e79838d1dd7ab4a8b4965e1c7f96 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 08:50:57 +0200 Subject: Fix project name guessing Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 9d52eada42..551573afc5 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -909,7 +909,12 @@ class P4Sync(Command): def guessProjectName(self): for p in self.depotPaths: - return p [p.strip().rfind("/") + 1:] + if p.endswith("/"): + p = p[:-1] + p = p[p.strip().rfind("/") + 1:] + if not p.endswith("/"): + p += "/" + return p def getBranchMapping(self): for info in p4CmdList("branches"): -- cgit v1.2.3 From 86fda6a327ce9355ae9eab69fc7a3ec33fb5d7d1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 08:54:45 +0200 Subject: Fix depot-paths encoding for multi-path imports (don't split up //depot/path/foo) Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 551573afc5..815d8bbb5d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1312,7 +1312,7 @@ class P4Sync(Command): parent = self.initialParents[branch] del self.initialParents[branch] - self.commit(description, filesForCommit, branch, branchPrefix, parent) + self.commit(description, filesForCommit, branch, [branchPrefix], parent) else: files = self.extractFilesFromCommit(description) self.commit(description, files, self.branch, self.depotPaths, -- cgit v1.2.3 From a43ff00c7c2981426d06b3621bbf62476aa5ec0d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 09:59:27 +0200 Subject: Fix support for explicit disabling of syncing with the origin Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 815d8bbb5d..ff56181310 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1028,6 +1028,8 @@ class P4Sync(Command): self.knownBranches = {} self.initialParents = {} self.hasOrigin = gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") + if not self.syncWithOrigin: + self.hasOrigin = False if self.importIntoRemotes: self.refPrefix = "refs/remotes/p4/" -- cgit v1.2.3 From 6581de096e8323385b8ec7d467e91927a80ce3b9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 10:01:58 +0200 Subject: Write out the options tag in the log message of imports only if we actually have options Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ff56181310..e380c149b4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -765,12 +765,11 @@ class P4Sync(Command): self.gitStream.write("data < 0: + self.gitStream.write(": options = %s" % details['options']) + self.gitStream.write("]\nEOT\n\n") if len(parent) > 0: if self.verbose: -- cgit v1.2.3 From c3bf3f1301319faab7344e2d8b5ab10a3d648856 Mon Sep 17 00:00:00 2001 From: Kevin Green Date: Mon, 11 Jun 2007 16:48:07 -0400 Subject: git-p4: check for existence of repo dir before trying to create When using git-p4 in this manner: git-p4 clone //depot/path/project myproject If "myproject" already exists as a dir, but not a valid git repo, it fails to create the directory. Signed-off-by: Kevin Green --- contrib/fast-import/git-p4 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e380c149b4..cababc7fc8 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1400,7 +1400,8 @@ class P4Clone(P4Sync): self.cloneDestination = self.defaultDestination(args) print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination) - os.makedirs(self.cloneDestination) + if not os.path.exists(self.cloneDestination): + os.makedirs(self.cloneDestination) os.chdir(self.cloneDestination) system("git init") self.gitdir = os.getcwd() + "/.git" -- cgit v1.2.3 From a9d1a27af1f1c997eeef67aed54136ae83355bb9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 23:28:03 +0200 Subject: Provide some information for single branch imports where the commits go Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index cababc7fc8..6c199296d3 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1175,7 +1175,7 @@ class P4Sync(Command): self.gitError = importProcess.stderr if self.revision: - print "Doing initial import of %s from revision %s" % (' '.join(self.depotPaths), self.revision) + print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), self.revision, self.branch) details = { "user" : "git perforce import user", "time" : int(time.time()) } details["desc"] = ("Initial import of %s from the state at revision %s" @@ -1252,6 +1252,9 @@ class P4Sync(Command): print "No changes to import!" return True + if not self.silent and not self.detectBranches: + print "Import destination: %s" % self.branch + self.updatedBranches = set() cnt = 1 -- cgit v1.2.3 From 81b462a629c2feff9ab1dc43148643aad9571271 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 23:30:23 +0200 Subject: Mention remotes/p4/master also in the documentation. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index c315158d8d..b16a8384bc 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -36,8 +36,8 @@ If you want more control you can also use the git-p4 sync command directly: git-p4 sync //path/in/your/perforce/depot This will import the current head revision of the specified depot path into a -"p4" branch of your git repository. You can use the --branch=mybranch option -to use a different branch. +"remotes/p4/master" branch of your git repository. You can use the +--branch=mybranch option to use a different branch. If you want to import the entire history of a given depot path just use @@ -57,7 +57,7 @@ newer changes from the Perforce depot by just calling git-p4 sync -in your git repository. By default the "p4" branch is updated. +in your git repository. By default the "remotes/p4/master" branch is updated. It is recommended to run 'git repack -a -d -f' from time to time when using incremental imports to optimally combine the individual git packs that each -- cgit v1.2.3 From e6b711f00e4578eb4b2127af12f73a5fa438f948 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 11 Jun 2007 23:40:25 +0200 Subject: git-p4 submit: Fix missing quotes around p4 commands to make them work with spaces in filenames Noticed by Alex Riesen Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6c199296d3..21f9ba7e07 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -391,10 +391,10 @@ class P4Submit(Command): system(applyPatchCmd) for f in filesToAdd: - system("p4 add %s" % f) + system("p4 add \"%s\"" % f) for f in filesToDelete: - system("p4 revert %s" % f) - system("p4 delete %s" % f) + system("p4 revert \"%s\"" % f) + system("p4 delete \"%s\"" % f) logMessage = "" if not self.directSubmit: -- cgit v1.2.3 From 27d2d8119bf985a0b59152737316f04f871e03f7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Jun 2007 14:31:59 +0200 Subject: Moved the code from git-p4 submit to figure out the upstream branch point into a separate helper method. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 21f9ba7e07..1c7db11529 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -168,6 +168,28 @@ def gitBranchExists(branch): def gitConfig(key): return read_pipe("git config %s" % key, ignore_error=True).strip() +def findUpstreamBranchPoint(): + settings = None + branchPoint = "" + parent = 0 + while parent < 65535: + commit = "HEAD~%s" % parent + log = extractLogMessageFromGitCommit(commit) + settings = extractSettingsGitLog(log) + if not settings.has_key("depot-paths"): + parent = parent + 1 + continue + + names = read_pipe_lines("git name-rev '--refs=refs/remotes/p4/*' '%s'" % commit) + if len(names) <= 0: + continue + + # strip away the beginning of 'HEAD~42 refs/remotes/p4/foo' + branchPoint = names[0].strip()[len(commit) + 1:] + break + + return [branchPoint, settings] + class Command: def __init__(self): self.usage = "usage: %prog [options]" @@ -494,25 +516,10 @@ class P4Submit(Command): else: return False - depotPath = "" - parent = 0 - while parent < 65535: - commit = "HEAD~%s" % parent - log = extractLogMessageFromGitCommit(commit) - settings = extractSettingsGitLog(log) - if not settings.has_key("depot-paths"): - parent = parent + 1 - continue - - depotPath = settings['depot-paths'][0] - - if len(self.origin) == 0: - names = read_pipe_lines("git name-rev '--refs=refs/remotes/p4/*' '%s'" % commit) - if len(names) > 0: - # strip away the beginning of 'HEAD~42 refs/remotes/p4/foo' - self.origin = names[0].strip()[len(commit) + 1:] - - break + [upstream, settings] = findUpstreamBranchPoint() + depotPath = settings['depot-paths'][0] + if len(self.origin) == 0: + self.origin = upstream if self.verbose: print "Origin branch is " + self.origin -- cgit v1.2.3 From d7e3868cdfdc73c3de15296ecf32138a8308c07e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 12 Jun 2007 14:34:46 +0200 Subject: Fix git-p4 rebase to detect the correct upstream branch instead of unconditionally always rebasing on top of remotes/p4/master Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1c7db11529..1168704be4 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1361,9 +1361,17 @@ class P4Rebase(Command): def run(self, args): sync = P4Sync() sync.run([]) - print "Rebasing the current branch" + + [upstream, settings] = findUpstreamBranchPoint() + if len(upstream) == 0: + die("Cannot find upstream branchpoint for rebase") + + # the branchpoint may be p4/foo~3, so strip off the parent + upstream = re.sub("~[0-9]+$", "", upstream) + + print "Rebasing the current branch onto %s" % upstream oldHead = read_pipe("git rev-parse HEAD").strip() - system("git rebase p4") + system("git rebase %s" % upstream) system("git diff-tree --stat --summary -M %s HEAD" % oldHead) return True -- cgit v1.2.3 From cbae7080a7fe0586255e85e1d14f1011260e8eee Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Tue, 12 Jun 2007 15:27:52 +0200 Subject: Only use double quotes on Windows Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1168704be4..b3f27fe90f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -180,7 +180,7 @@ def findUpstreamBranchPoint(): parent = parent + 1 continue - names = read_pipe_lines("git name-rev '--refs=refs/remotes/p4/*' '%s'" % commit) + names = read_pipe_lines("git name-rev \"--refs=refs/remotes/p4/*\" \"%s\"" % commit) if len(names) <= 0: continue -- cgit v1.2.3 From 30a844874db6ee25b6f54ea786f0adabd8e074d7 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Wed, 13 Jun 2007 14:16:15 +0530 Subject: gitview: Fix the blame interface. The async reading from the pipe was skipping some of the input lines. Fix the same by making sure that we add the partial content of the previous read to the newly read data. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 098cb01353..93ecfc1bb9 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -352,6 +352,7 @@ class AnnotateWindow(object): self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_border_width(0) self.window.set_title("Git repository browser annotation window") + self.prev_read = "" # Use two thirds of the screen by default screen = self.window.get_screen() @@ -401,7 +402,10 @@ class AnnotateWindow(object): def data_ready(self, source, condition): while (1): try : - buffer = source.read(8192) + # A simple readline doesn't work + # a readline bug ?? + buffer = source.read(100) + except: # resource temporary not available return True @@ -411,6 +415,19 @@ class AnnotateWindow(object): source.close() return False + if (self.prev_read != ""): + buffer = self.prev_read + buffer + self.prev_read = "" + + if (buffer[len(buffer) -1] != '\n'): + try: + newline_index = buffer.rindex("\n") + except ValueError: + newline_index = 0 + + self.prev_read = buffer[newline_index:(len(buffer))] + buffer = buffer[0:newline_index] + for buff in buffer.split("\n"): annotate_line = re.compile('^([0-9a-f]{40}) (.+) (.+) (.+)$') m = annotate_line.match(buff) -- cgit v1.2.3 From 1be846f6e48bae57c3d5cc346300eaf9550f6c8d Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Wed, 13 Jun 2007 14:16:16 +0530 Subject: gitview: run blame with -C -C pass -C -C option to git-blame so that blame browsing works when the data is copied over from other files. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 93ecfc1bb9..5931766620 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -533,7 +533,7 @@ class AnnotateWindow(object): self.add_file_data(filename, commit_sha1, line_num) - fp = os.popen("git blame --incremental -- " + filename + " " + commit_sha1) + fp = os.popen("git blame --incremental -C -C -- " + 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) -- cgit v1.2.3 From 3c699645f589612065b048ecde45a4ea293dc75f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 16 Jun 2007 13:09:21 +0200 Subject: Fix initial multi-branch import. The list of existing p4 branches in git wasn't initialized. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b3f27fe90f..e527734be5 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -637,6 +637,7 @@ class P4Sync(Command): self.isWindows = (platform.system() == "Windows") self.keepRepoPath = False self.depotPaths = None + self.p4BranchesInGit = [] if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False -- cgit v1.2.3 From da4a660161cfe9d04c0849d77fa460c6ffc6503c Mon Sep 17 00:00:00 2001 From: Benjamin Sergeant Date: Fri, 8 Jun 2007 11:13:55 -0700 Subject: git-p4 fails when cloning a p4 depo. A perforce command with all the files in the repo is generated to get all the file content. Here is a patch to break it into multiple successive perforce command who uses 4K of parameter max, and collect the output for later. It works, but not for big depos, because the whole perforce depo content is stored in memory in P4Sync.run(), and it looks like mine is bigger than 2 Gigs, so I had to kill the process. [Simon: I added the bit about using SC_ARG_MAX, as suggested by Han-Wen] Signed-off-by: Benjamin Sergeant Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e527734be5..d1f8d3b78d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -711,9 +711,23 @@ class P4Sync(Command): if not files: return - filedata = p4CmdList('print %s' % ' '.join(['"%s#%s"' % (f['path'], - f['rev']) - for f in files])) + # We cannot put all the files on the command line + # OS have limitations on the max lenght of arguments + # POSIX says it's 4096 bytes, default for Linux seems to be 130 K. + # and all OS from the table below seems to be higher than POSIX. + # See http://www.in-ulm.de/~mascheck/various/argmax/ + argmax = min(4000, os.sysconf('SC_ARG_MAX')) + chunk = '' + filedata = [] + for i in xrange(len(files)): + f = files[i] + chunk += '"%s#%s" ' % (f['path'], f['rev']) + if len(chunk) > argmax or i == len(files)-1: + data = p4CmdList('print %s' % chunk) + if "p4ExitCode" in data[0]: + die("Problems executing p4. Error: [%d]." % (data[0]['p4ExitCode'])); + filedata.extend(data) + chunk = '' j = 0; contents = {} -- cgit v1.2.3 From 6555b2ccfe63913b3e5c8b02e117f0f476307ca2 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 17 Jun 2007 11:25:34 +0200 Subject: Fix the branch mapping detection to be independent from the order of the "p4 branches" output. Collect "unknown" source branches separately and register them at the end. Also added a minor speed up to splitFilesIntoBranches by breaking out of the loop through all branches when it's safe. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d1f8d3b78d..3b6d8a09d1 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -700,6 +700,7 @@ class P4Sync(Command): if branch not in branches: branches[branch] = [] branches[branch].append(file) + break return branches @@ -938,6 +939,8 @@ class P4Sync(Command): return p def getBranchMapping(self): + lostAndFoundBranches = set() + for info in p4CmdList("branches"): details = p4Cmd("branch -o %s" % info["branch"]) viewIdx = 0 @@ -953,10 +956,17 @@ class P4Sync(Command): if source.startswith(self.depotPaths[0]) and destination.startswith(self.depotPaths[0]): source = source[len(self.depotPaths[0]):-4] destination = destination[len(self.depotPaths[0]):-4] - if destination not in self.knownBranches: - self.knownBranches[destination] = source + + self.knownBranches[destination] = source + + lostAndFoundBranches.discard(destination) + if source not in self.knownBranches: - self.knownBranches[source] = source + lostAndFoundBranches.add(source) + + + for branch in lostAndFoundBranches: + self.knownBranches[branch] = branch def listExistingP4GitBranches(self): self.p4BranchesInGit = [] -- cgit v1.2.3 From 1a2edf4e8dff05fea66daf82c675cfab673c1242 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 17 Jun 2007 15:10:24 +0200 Subject: Warn about conflicting p4 branch mappings and use the first one found. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3b6d8a09d1..2040591383 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -957,6 +957,12 @@ class P4Sync(Command): source = source[len(self.depotPaths[0]):-4] destination = destination[len(self.depotPaths[0]):-4] + if destination in self.knownBranches: + if not self.silent: + print "p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination) + print "but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination) + continue + self.knownBranches[destination] = source lostAndFoundBranches.discard(destination) -- cgit v1.2.3 From 09d89de2e31ed4d62b6ff344e9ad9ef29550121b Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 20 Jun 2007 23:10:28 +0200 Subject: Added git-p4 branches command that shows the mapping of perforce depot paths to imported git branches. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2040591383..16de15c4ea 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1465,6 +1465,31 @@ class P4Clone(P4Sync): return True +class P4Branches(Command): + def __init__(self): + Command.__init__(self) + self.options = [ ] + self.description = ("Shows the git branches that hold imports and their " + + "corresponding perforce depot paths") + self.verbose = False + + def run(self, args): + cmdline = "git rev-parse --symbolic " + cmdline += " --remotes" + + for line in read_pipe_lines(cmdline): + line = line.strip() + + if not line.startswith('p4/') or line == "p4/HEAD": + continue + branch = line + + log = extractLogMessageFromGitCommit("refs/remotes/%s" % branch) + settings = extractSettingsGitLog(log) + + print "%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"]) + return True + class HelpFormatter(optparse.IndentedHelpFormatter): def __init__(self): optparse.IndentedHelpFormatter.__init__(self) @@ -1489,7 +1514,8 @@ commands = { "sync" : P4Sync, "rebase" : P4Rebase, "clone" : P4Clone, - "rollback" : P4RollBack + "rollback" : P4RollBack, + "branches" : P4Branches } -- cgit v1.2.3 From 9ceab36375dfd4088b265033e1de9225b3527cab Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 22 Jun 2007 00:01:57 +0200 Subject: Make it possible to specify the HEAD for the internal findUpstreamBranchPoint function. This isn't used right now in git-p4 but I use it in an external script that loads git-p4 as module. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 16de15c4ea..54a05eb99c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -168,12 +168,12 @@ def gitBranchExists(branch): def gitConfig(key): return read_pipe("git config %s" % key, ignore_error=True).strip() -def findUpstreamBranchPoint(): +def findUpstreamBranchPoint(head = "HEAD"): settings = None branchPoint = "" parent = 0 while parent < 65535: - commit = "HEAD~%s" % parent + commit = head + "~%s" % parent log = extractLogMessageFromGitCommit(commit) settings = extractSettingsGitLog(log) if not settings.has_key("depot-paths"): -- cgit v1.2.3 From 92d7c8e37b96244f2c55f5c66ec52c6325acca1d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 22 Jun 2007 18:44:04 -0400 Subject: Avoid src:dst syntax as default bash completion for git push Raimund Bauer just discovered that the default bash completion for a local branch name in a git-push line is not the best choice when the branch does not exist on the remote system. In the past we have always completed the local name 'test' as "test:test", indicating that the destination name is the same as the local name. But this fails when "test" does not yet exist on the remote system, as there is no "test" branch for it to match the name against. Fortunately git-push does the right thing when given just the local branch, as it assumes you want to use the same name in the destination repository. So we now offer "test" as the completion in a git-push line, and let git-push assume that is also the remote branch name. We also still support the remote branch completion after the :, but only if the user manually adds the colon before trying to get a completion. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9e72f0f7b1..c7c9963347 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -683,7 +683,7 @@ _git_push () __gitcomp "$(__git_refs "$remote")" "" "${cur#*:}" ;; *) - __gitcomp "$(__git_refs2)" + __gitcomp "$(__git_refs)" ;; esac ;; -- cgit v1.2.3 From 09381b458f5f1ac1a78df1aaeeb53aa79fcf2c4f Mon Sep 17 00:00:00 2001 From: Julian Phillips Date: Tue, 19 Jun 2007 12:44:43 +0100 Subject: new-workdir: handle rev-parse --git-dir not always giving full path rev-parse --git-dir outputs a full path - except for the single case of when the path would be $(pwd)/.git, in which case it outputs simply .git. Check for this special case and handle it. Signed-off-by: Julian Phillips Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index f2a3615bbc..709b2a3ac0 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -24,6 +24,11 @@ git_dir=$(cd "$orig_git" 2>/dev/null && git rev-parse --git-dir 2>/dev/null) || die "\"$orig_git\" is not a git repository!" +if test "$git_dir" == ".git" +then + git_dir="$orig_git/.git" +fi + # don't link to a workdir if test -L "$git_dir/config" then -- cgit v1.2.3 From 161fea832a8e8ea7d6cdc1bdc388f19825641a53 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 24 Jun 2007 19:42:16 -0400 Subject: Teach bash how to complete +refspec on git-push Using `git push origin +foo` to forcefully overwrite the remote branch named foo is a common idiom, especially since + is shorter than the long option --force and can be specified on a per-branch basis. We now complete `git push origin +foo` just like we do the standard `git push origin foo`. The leading + on a branch refspec does not alter the completion. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c7c9963347..f2b10fa5f6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -682,6 +682,9 @@ _git_push () esac __gitcomp "$(__git_refs "$remote")" "" "${cur#*:}" ;; + +*) + __gitcomp "$(__git_refs)" + "${cur#+}" + ;; *) __gitcomp "$(__git_refs)" ;; -- cgit v1.2.3 From b658d50325c938d87d4891cb62e2eefb0b58a62c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 25 Jun 2007 13:04:26 +0200 Subject: git-new-workdir: Fix shell warning about operator == used with test. Use = instead of == with test to test for equality. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 709b2a3ac0..3ff6bd166a 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -24,7 +24,7 @@ git_dir=$(cd "$orig_git" 2>/dev/null && git rev-parse --git-dir 2>/dev/null) || die "\"$orig_git\" is not a git repository!" -if test "$git_dir" == ".git" +if test "$git_dir" = ".git" then git_dir="$orig_git/.git" fi -- cgit v1.2.3 From 5be60078c935ed08ee8eb5a32680bdfb6bb5bdf3 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 2 Jul 2007 22:52:14 -0700 Subject: Rewrite "git-frotz" to "git frotz" This uses the remove-dashes target to replace "git-frotz" to "git frotz". Signed-off-by: Junio C Hamano --- contrib/examples/git-gc.sh | 8 ++++---- contrib/examples/git-resolve.sh | 34 +++++++++++++++++----------------- contrib/remotes2config.sh | 4 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-gc.sh b/contrib/examples/git-gc.sh index 436d7caff5..2ae235b081 100755 --- a/contrib/examples/git-gc.sh +++ b/contrib/examples/git-gc.sh @@ -30,8 +30,8 @@ notbare|"") esac test "true" != "$pack_refs" || -git-pack-refs --prune && -git-reflog expire --all && +git pack-refs --prune && +git reflog expire --all && git-repack -a -d -l && -$no_prune git-prune && -git-rerere gc || exit +$no_prune git prune && +git rerere gc || exit diff --git a/contrib/examples/git-resolve.sh b/contrib/examples/git-resolve.sh index 36b90e3849..0ee1bd898e 100755 --- a/contrib/examples/git-resolve.sh +++ b/contrib/examples/git-resolve.sh @@ -17,8 +17,8 @@ dropheads() { "$GIT_DIR/LAST_MERGE" || exit 1 } -head=$(git-rev-parse --verify "$1"^0) && -merge=$(git-rev-parse --verify "$2"^0) && +head=$(git rev-parse --verify "$1"^0) && +merge=$(git rev-parse --verify "$2"^0) && merge_name="$2" && merge_msg="$3" || usage @@ -34,7 +34,7 @@ dropheads echo $head > "$GIT_DIR"/ORIG_HEAD echo $merge > "$GIT_DIR"/LAST_MERGE -common=$(git-merge-base $head $merge) +common=$(git merge-base $head $merge) if [ -z "$common" ]; then die "Unable to find common commit between" $merge $head fi @@ -46,11 +46,11 @@ case "$common" in exit 0 ;; "$head") - echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $merge)" - git-read-tree -u -m $head $merge || exit 1 - git-update-ref -m "resolve $merge_name: Fast forward" \ + echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $merge)" + git read-tree -u -m $head $merge || exit 1 + git update-ref -m "resolve $merge_name: Fast forward" \ HEAD "$merge" "$head" - git-diff-tree -p $head $merge | git-apply --stat + git diff-tree -p $head $merge | git apply --stat dropheads exit 0 ;; @@ -62,7 +62,7 @@ git var GIT_COMMITTER_IDENT >/dev/null || exit # Find an optimum merge base if there are more than one candidates. LF=' ' -common=$(git-merge-base -a $head $merge) +common=$(git merge-base -a $head $merge) case "$common" in ?*"$LF"?*) echo "Trying to find the optimum merge base." @@ -72,10 +72,10 @@ case "$common" in for c in $common do rm -f $G - GIT_INDEX_FILE=$G git-read-tree -m $c $head $merge \ + GIT_INDEX_FILE=$G git read-tree -m $c $head $merge \ 2>/dev/null || continue # Count the paths that are unmerged. - cnt=`GIT_INDEX_FILE=$G git-ls-files --unmerged | wc -l` + cnt=`GIT_INDEX_FILE=$G git ls-files --unmerged | wc -l` if test $best_cnt -le 0 -o $cnt -le $best_cnt then best=$c @@ -92,9 +92,9 @@ case "$common" in esac echo "Trying to merge $merge into $head using $common." -git-update-index --refresh 2>/dev/null -git-read-tree -u -m $common $head $merge || exit 1 -result_tree=$(git-write-tree 2> /dev/null) +git update-index --refresh 2>/dev/null +git read-tree -u -m $common $head $merge || exit 1 +result_tree=$(git write-tree 2> /dev/null) if [ $? -ne 0 ]; then echo "Simple merge failed, trying Automatic merge" git-merge-index -o git-merge-one-file -a @@ -102,11 +102,11 @@ if [ $? -ne 0 ]; then echo $merge > "$GIT_DIR"/MERGE_HEAD die "Automatic merge failed, fix up by hand" fi - result_tree=$(git-write-tree) || exit 1 + result_tree=$(git write-tree) || exit 1 fi -result_commit=$(echo "$merge_msg" | git-commit-tree $result_tree -p $head -p $merge) +result_commit=$(echo "$merge_msg" | git commit-tree $result_tree -p $head -p $merge) echo "Committed merge $result_commit" -git-update-ref -m "resolve $merge_name: In-index merge" \ +git update-ref -m "resolve $merge_name: In-index merge" \ HEAD "$result_commit" "$head" -git-diff-tree -p $head $result_commit | git-apply --stat +git diff-tree -p $head $result_commit | git apply --stat dropheads diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh index 0c8b954490..5838b3ab05 100644 --- a/contrib/remotes2config.sh +++ b/contrib/remotes2config.sh @@ -26,8 +26,8 @@ if [ -d "$GIT_DIR"/remotes ]; then mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old fi ;; *) - echo "git-config $key "$value" $regex" - git-config $key "$value" $regex || error=1 ;; + echo "git config $key "$value" $regex" + git config $key "$value" $regex || error=1 ;; esac done fi -- cgit v1.2.3 From b4372ef136b0a5a2c1dbd88a11dd72b478d0e0a5 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 6 Jul 2007 13:05:59 +0100 Subject: Enable "git rerere" by the config variable rerere.enabled Earlier, "git rerere" was enabled by creating the directory .git/rr-cache. That is definitely not in line with most other features, which are enabled by a config variable. So, check the config variable "rerere.enabled". If it is set to "false" explicitely, do not activate rerere, even if .git/rr-cache exists. This should help when you want to disable rerere temporarily. If "rerere.enabled" is not set at all, fall back to detection of the directory .git/rr-cache. [jc: with minimum tweaks] Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index f60017948f..457f95fc05 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -681,8 +681,7 @@ and returns the process output as a string." (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) (git-set-files-state files 'uptodate) - (when (file-directory-p ".git/rr-cache") - (git-run-command nil nil "rerere")) + (git-run-command nil nil "rerere") (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) -- cgit v1.2.3 From 73f893605050c291dd8c4d8f3f50b8fec47dcf51 Mon Sep 17 00:00:00 2001 From: Brian Downing Date: Wed, 11 Jul 2007 22:02:25 -0500 Subject: Pack information tool This tool will print vaguely pretty information about a pack. It expects the output of "git-verify-pack -v" as input on stdin. $ git-verify-pack -v | packinfo.pl See the documentation in the script (contrib/stats/packinfo.pl) for more information. Signed-off-by: Brian Downing Signed-off-by: Junio C Hamano --- contrib/stats/packinfo.pl | 212 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100755 contrib/stats/packinfo.pl (limited to 'contrib') diff --git a/contrib/stats/packinfo.pl b/contrib/stats/packinfo.pl new file mode 100755 index 0000000000..792673a325 --- /dev/null +++ b/contrib/stats/packinfo.pl @@ -0,0 +1,212 @@ +#!/bin/perl +# +# This tool will print vaguely pretty information about a pack. It +# expects the output of "git-verify-pack -v" as input on stdin. +# +# $ git-verify-pack -v | packinfo.pl +# +# This prints some full-pack statistics; currently "all sizes", "all +# path sizes", "tree sizes", "tree path sizes", and "depths". +# +# * "all sizes" stats are across every object size in the file; +# full sizes for base objects, and delta size for deltas. +# * "all path sizes" stats are across all object's "path sizes". +# A path size is the sum of the size of the delta chain, including the +# base object. In other words, it's how many bytes need be read to +# reassemble the file from deltas. +# * "tree sizes" are object sizes grouped into delta trees. +# * "tree path sizes" are path sizes grouped into delta trees. +# * "depths" should be obvious. +# +# When run as: +# +# $ git-verify-pack -v | packinfo.pl -tree +# +# the trees of objects are output along with the stats. This looks +# like: +# +# 0 commit 031321c6... 803 803 +# +# 0 blob 03156f21... 1767 1767 +# 1 blob f52a9d7f... 10 1777 +# 2 blob a8cc5739... 51 1828 +# 3 blob 660e90b1... 15 1843 +# 4 blob 0cb8e3bb... 33 1876 +# 2 blob e48607f0... 311 2088 +# size: count 6 total 2187 min 10 max 1767 mean 364.50 median 51 std_dev 635.85 +# path size: count 6 total 11179 min 1767 max 2088 mean 1863.17 median 1843 std_dev 107.26 +# +# The first number after the sha1 is the object size, the second +# number is the path size. The statistics are across all objects in +# the previous delta tree. Obviously they are omitted for trees of +# one object. +# +# When run as: +# +# $ git-verify-pack -v | packinfo.pl -tree -filenames +# +# it adds filenames to the tree. Getting this information is slow: +# +# 0 blob 03156f21... 1767 1767 Documentation/git-lost-found.txt @ tags/v1.2.0~142 +# 1 blob f52a9d7f... 10 1777 Documentation/git-lost-found.txt @ tags/v1.5.0-rc1~74 +# 2 blob a8cc5739... 51 1828 Documentation/git-lost+found.txt @ tags/v0.99.9h^0 +# 3 blob 660e90b1... 15 1843 Documentation/git-lost+found.txt @ master~3222^2~2 +# 4 blob 0cb8e3bb... 33 1876 Documentation/git-lost+found.txt @ master~3222^2~3 +# 2 blob e48607f0... 311 2088 Documentation/git-lost-found.txt @ tags/v1.5.2-rc3~4 +# size: count 6 total 2187 min 10 max 1767 mean 364.50 median 51 std_dev 635.85 +# path size: count 6 total 11179 min 1767 max 2088 mean 1863.17 median 1843 std_dev 107.26 +# +# When run as: +# +# $ git-verify-pack -v | packinfo.pl -dump +# +# it prints out "sha1 size pathsize depth" for each sha1 in lexical +# order. +# +# 000079a2eaef17b7eae70e1f0f635557ea67b644 30 472 7 +# 00013cafe6980411aa6fdd940784917b5ff50f0a 44 1542 4 +# 000182eacf99cde27d5916aa415921924b82972c 499 499 0 +# ... +# +# This is handy for comparing two packs. Adding "-filenames" will add +# filenames, as per "-tree -filenames" above. + +use strict; +use Getopt::Long; + +my $filenames = 0; +my $tree = 0; +my $dump = 0; +GetOptions("tree" => \$tree, + "filenames" => \$filenames, + "dump" => \$dump); + +my %parents; +my %children; +my %sizes; +my @roots; +my %paths; +my %types; +my @commits; +my %names; +my %depths; +my @depths; + +while () { + my ($sha1, $type, $size, $offset, $depth, $parent) = split(/\s+/, $_); + next unless ($sha1 =~ /^[0-9a-f]{40}$/); + $depths{$sha1} = $depth || 0; + push(@depths, $depth || 0); + push(@commits, $sha1) if ($type eq 'commit'); + push(@roots, $sha1) unless $parent; + $parents{$sha1} = $parent; + $types{$sha1} = $type; + push(@{$children{$parent}}, $sha1); + $sizes{$sha1} = $size; +} + +if ($filenames && ($tree || $dump)) { + open(NAMES, "git-name-rev --all|"); + while () { + if (/^(\S+)\s+(.*)$/) { + my ($sha1, $name) = ($1, $2); + $names{$sha1} = $name; + } + } + close NAMES; + + for my $commit (@commits) { + my $name = $names{$commit}; + open(TREE, "git-ls-tree -t -r $commit|"); + print STDERR "Plumbing tree $name\n"; + while () { + if (/^(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) { + my ($mode, $type, $sha1, $path) = ($1, $2, $3, $4); + $paths{$sha1} = "$path @ $name"; + } + } + close TREE; + } +} + +sub stats { + my @data = sort {$a <=> $b} @_; + my $min = $data[0]; + my $max = $data[$#data]; + my $total = 0; + my $count = scalar @data; + for my $datum (@data) { + $total += $datum; + } + my $mean = $total / $count; + my $median = $data[int(@data / 2)]; + my $diff_sum = 0; + for my $datum (@data) { + $diff_sum += ($datum - $mean)**2; + } + my $std_dev = sqrt($diff_sum / $count); + return ($count, $total, $min, $max, $mean, $median, $std_dev); +} + +sub print_stats { + my $name = shift; + my ($count, $total, $min, $max, $mean, $median, $std_dev) = stats(@_); + printf("%s: count %s total %s min %s max %s mean %.2f median %s std_dev %.2f\n", + $name, $count, $total, $min, $max, $mean, $median, $std_dev); +} + +my @sizes; +my @path_sizes; +my @all_sizes; +my @all_path_sizes; +my %path_sizes; + +sub dig { + my ($sha1, $depth, $path_size) = @_; + $path_size += $sizes{$sha1}; + push(@sizes, $sizes{$sha1}); + push(@all_sizes, $sizes{$sha1}); + push(@path_sizes, $path_size); + push(@all_path_sizes, $path_size); + $path_sizes{$sha1} = $path_size; + if ($tree) { + printf("%3d%s %6s %s %8d %8d %s\n", + $depth, (" " x $depth), $types{$sha1}, + $sha1, $sizes{$sha1}, $path_size, $paths{$sha1}); + } + for my $child (@{$children{$sha1}}) { + dig($child, $depth + 1, $path_size); + } +} + +my @tree_sizes; +my @tree_path_sizes; + +for my $root (@roots) { + undef @sizes; + undef @path_sizes; + dig($root, 0, 0); + my ($aa, $sz_total) = stats(@sizes); + my ($bb, $psz_total) = stats(@path_sizes); + push(@tree_sizes, $sz_total); + push(@tree_path_sizes, $psz_total); + if ($tree) { + if (@sizes > 1) { + print_stats(" size", @sizes); + print_stats("path size", @path_sizes); + } + print "\n"; + } +} + +if ($dump) { + for my $sha1 (sort keys %sizes) { + print "$sha1 $sizes{$sha1} $path_sizes{$sha1} $depths{$sha1} $paths{$sha1}\n"; + } +} else { + print_stats(" all sizes", @all_sizes); + print_stats(" all path sizes", @all_path_sizes); + print_stats(" tree sizes", @tree_sizes); + print_stats("tree path sizes", @tree_path_sizes); + print_stats(" depths", @depths); +} -- cgit v1.2.3 From 750bd6ac350079cf1f76c24acb7686ad89a95102 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 12 Jul 2007 03:40:18 -0400 Subject: script to display a distribution of longest common hash prefixes This script was originally posted on the git mailing list by Randal L. Schwartz . Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- contrib/stats/git-common-hash | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100755 contrib/stats/git-common-hash (limited to 'contrib') diff --git a/contrib/stats/git-common-hash b/contrib/stats/git-common-hash new file mode 100755 index 0000000000..e27fd088be --- /dev/null +++ b/contrib/stats/git-common-hash @@ -0,0 +1,26 @@ +#!/bin/sh + +# This script displays the distribution of longest common hash prefixes. +# This can be used to determine the minimum prefix length to use +# for object names to be unique. + +git rev-list --objects --all | sort | perl -lne ' + substr($_, 40) = ""; + # uncomment next line for a distribution of bits instead of hex chars + # $_ = unpack("B*",pack("H*",$_)); + if (defined $p) { + ($p ^ $_) =~ /^(\0*)/; + $common = length $1; + if (defined $pcommon) { + $count[$pcommon > $common ? $pcommon : $common]++; + } else { + $count[$common]++; # first item + } + } + $p = $_; + $pcommon = $common; + END { + $count[$common]++; # last item + print "$_: $count[$_]" for 0..$#count; + } +' -- cgit v1.2.3 From b492bbd8362840f9ac1e5389cdfaa8dd73ca793b Mon Sep 17 00:00:00 2001 From: Brian Downing Date: Thu, 12 Jul 2007 09:16:11 -0500 Subject: Correct shebang line for contrib/stats/packinfo.pl "/bin/perl"? What was I thinking? Signed-off-by: Brian Downing Signed-off-by: Junio C Hamano --- contrib/stats/packinfo.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/stats/packinfo.pl b/contrib/stats/packinfo.pl index 792673a325..aab501ea08 100755 --- a/contrib/stats/packinfo.pl +++ b/contrib/stats/packinfo.pl @@ -1,4 +1,4 @@ -#!/bin/perl +#!/usr/bin/perl # # This tool will print vaguely pretty information about a pack. It # expects the output of "git-verify-pack -v" as input on stdin. -- cgit v1.2.3 From 248c648a0de945ec37af13e0a9b8671da525c323 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Thu, 12 Jul 2007 16:48:48 +0200 Subject: Add missing functions to contrib/emacs/vc-git.el This is necessary to make several editing functions work, like C-u C-x v = Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- contrib/emacs/vc-git.el | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el index e456ab9712..b8f6be5c0a 100644 --- a/contrib/emacs/vc-git.el +++ b/contrib/emacs/vc-git.el @@ -81,6 +81,71 @@ (match-string 2 str) str))) +(defun vc-git-symbolic-commit (commit) + "Translate COMMIT string into symbolic form. +Returns nil if not possible." + (and commit + (with-temp-buffer + (and + (zerop + (call-process "git" nil '(t nil) nil "name-rev" + "--name-only" "--tags" + commit)) + (goto-char (point-min)) + (= (forward-line 2) 1) + (bolp) + (buffer-substring-no-properties (point-min) (1- (point-max))))))) + +(defun vc-git-previous-version (file rev) + "git-specific version of `vc-previous-version'." + (let ((default-directory (file-name-directory (expand-file-name file))) + (file (file-name-nondirectory file))) + (vc-git-symbolic-commit + (with-temp-buffer + (and + (zerop + (call-process "git" nil '(t nil) nil "rev-list" + "-2" rev "--" file)) + (goto-char (point-max)) + (bolp) + (zerop (forward-line -1)) + (not (bobp)) + (buffer-substring-no-properties + (point) + (1- (point-max)))))))) + +(defun vc-git-next-version (file rev) + "git-specific version of `vc-next-version'." + (let* ((default-directory (file-name-directory + (expand-file-name file))) + (file (file-name-nondirectory file)) + (current-rev + (with-temp-buffer + (and + (zerop + (call-process "git" nil '(t nil) nil "rev-list" + "-1" rev "--" file)) + (goto-char (point-max)) + (bolp) + (zerop (forward-line -1)) + (bobp) + (buffer-substring-no-properties + (point) + (1- (point-max))))))) + (and current-rev + (vc-git-symbolic-commit + (with-temp-buffer + (and + (zerop + (call-process "git" nil '(t nil) nil "rev-list" + "HEAD" "--" file)) + (goto-char (point-min)) + (search-forward current-rev nil t) + (zerop (forward-line -1)) + (buffer-substring-no-properties + (point) + (progn (forward-line 1) (1- (point)))))))))) + (defun vc-git-revert (file &optional contents-done) "Revert FILE to the version stored in the git repository." (if contents-done -- cgit v1.2.3 From af6861b14488a91ab932c63852979eaf78b549d2 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 14 Jul 2007 13:43:09 -0700 Subject: Add contrib/stats/mailmap.pl script This script reads the existing commit log and .mailmap file, and outputs author e-mail addresses that would map to more than one names (most likely due to difference in the way they are spelled, but some are due to ancient botched commits). Signed-off-by: Junio C Hamano --- contrib/stats/mailmap.pl | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 contrib/stats/mailmap.pl (limited to 'contrib') diff --git a/contrib/stats/mailmap.pl b/contrib/stats/mailmap.pl new file mode 100755 index 0000000000..4b852e2455 --- /dev/null +++ b/contrib/stats/mailmap.pl @@ -0,0 +1,38 @@ +#!/usr/bin/perl -w +my %mailmap = (); +open I, "<", ".mailmap"; +while () { + chomp; + next if /^#/; + if (my ($author, $mail) = /^(.*?)\s+<(.+)>$/) { + $mailmap{$mail} = $author; + } +} +close I; + +my %mail2author = (); +open I, "git log --pretty='format:%ae %an' |"; +while () { + chomp; + my ($mail, $author) = split(/\t/, $_); + next if exists $mailmap{$mail}; + $mail2author{$mail} ||= {}; + $mail2author{$mail}{$author} ||= 0; + $mail2author{$mail}{$author}++; +} +close I; + +while (my ($mail, $authorcount) = each %mail2author) { + # %$authorcount is ($author => $count); + # sort and show the names from the most frequent ones. + my @names = (map { $_->[0] } + sort { $b->[1] <=> $a->[1] } + map { [$_, $authorcount->{$_}] } + keys %$authorcount); + if (1 < @names) { + for (@names) { + print "$_ <$mail>\n"; + } + } +} + -- cgit v1.2.3 From 48b4c3d5ab1610c6dc0198fe94334d78e8a82e16 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Fri, 13 Jul 2007 14:39:05 +0200 Subject: Fix git-p4 on Windows to not use the Posix sysconf function. Add condition for Windows, since it doesn't support the os.sysconf module. We hardcode the commandline limit to 2K, as that should work on most Windows platforms. Signed-off-by: Marius Storm-Olsen Acked-by: Simon Hausmann Signed-off-by: Shawn O. Pearce --- contrib/fast-import/git-p4 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 54a05eb99c..d877150f41 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -717,7 +717,11 @@ class P4Sync(Command): # POSIX says it's 4096 bytes, default for Linux seems to be 130 K. # and all OS from the table below seems to be higher than POSIX. # See http://www.in-ulm.de/~mascheck/various/argmax/ - argmax = min(4000, os.sysconf('SC_ARG_MAX')) + if (self.isWindows): + argmax = 2000 + else: + argmax = min(4000, os.sysconf('SC_ARG_MAX')) + chunk = '' filedata = [] for i in xrange(len(files)): -- cgit v1.2.3 From 4cb08df553e270ab516a140caeeddd8e94f1e497 Mon Sep 17 00:00:00 2001 From: Emil Medve Date: Sat, 14 Jul 2007 12:51:44 -0500 Subject: Use $(RM) in Makefiles instead of 'rm -f' Signed-off-by: Emil Medve Signed-off-by: Junio C Hamano --- contrib/emacs/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile index 98aa0aae9b..5e94d6fcd3 100644 --- a/contrib/emacs/Makefile +++ b/contrib/emacs/Makefile @@ -7,6 +7,7 @@ INSTALL ?= install INSTALL_ELC = $(INSTALL) -m 644 prefix ?= $(HOME) emacsdir = $(prefix)/share/emacs/site-lisp +RM ?= rm -f all: $(ELC) @@ -17,4 +18,4 @@ install: all %.elc: %.el $(EMACS) -batch -f batch-byte-compile $< -clean:; rm -f $(ELC) +clean:; $(RM) $(ELC) -- cgit v1.2.3 From e3b4968f9caa04e83b2f27b64182187b13b86dab Mon Sep 17 00:00:00 2001 From: Sean Date: Sun, 15 Jul 2007 15:52:32 -0400 Subject: Demote git-p4import to contrib status. Move git-p4import.py and Documentation/git-p4import.txt into a contrib/p4import directory. Add a README there directing people to contrib/fast-import/git-p4 as a better alternative. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano --- contrib/p4import/README | 1 + contrib/p4import/git-p4import.py | 360 ++++++++++++++++++++++++++++++++++++++ contrib/p4import/git-p4import.txt | 167 ++++++++++++++++++ 3 files changed, 528 insertions(+) create mode 100644 contrib/p4import/README create mode 100644 contrib/p4import/git-p4import.py create mode 100644 contrib/p4import/git-p4import.txt (limited to 'contrib') diff --git a/contrib/p4import/README b/contrib/p4import/README new file mode 100644 index 0000000000..b9892b6793 --- /dev/null +++ b/contrib/p4import/README @@ -0,0 +1 @@ +Please see contrib/fast-import/git-p4 for a better Perforce importer. diff --git a/contrib/p4import/git-p4import.py b/contrib/p4import/git-p4import.py new file mode 100644 index 0000000000..0f3d97b67e --- /dev/null +++ b/contrib/p4import/git-p4import.py @@ -0,0 +1,360 @@ +#!/usr/bin/python +# +# This tool is copyright (c) 2006, Sean Estabrooks. +# It is released under the Gnu Public License, version 2. +# +# Import Perforce branches into Git repositories. +# Checking out the files is done by calling the standard p4 +# client which you must have properly configured yourself +# + +import marshal +import os +import sys +import time +import getopt + +from signal import signal, \ + SIGPIPE, SIGINT, SIG_DFL, \ + default_int_handler + +signal(SIGPIPE, SIG_DFL) +s = signal(SIGINT, SIG_DFL) +if s != default_int_handler: + signal(SIGINT, s) + +def die(msg, *args): + for a in args: + msg = "%s %s" % (msg, a) + print "git-p4import fatal error:", msg + sys.exit(1) + +def usage(): + print "USAGE: git-p4import [-q|-v] [--authors=] [-t ] [//p4repo/path ]" + sys.exit(1) + +verbosity = 1 +logfile = "/dev/null" +ignore_warnings = False +stitch = 0 +tagall = True + +def report(level, msg, *args): + global verbosity + global logfile + for a in args: + msg = "%s %s" % (msg, a) + fd = open(logfile, "a") + fd.writelines(msg) + fd.close() + if level <= verbosity: + print msg + +class p4_command: + def __init__(self, _repopath): + try: + global logfile + self.userlist = {} + if _repopath[-1] == '/': + self.repopath = _repopath[:-1] + else: + self.repopath = _repopath + if self.repopath[-4:] != "/...": + self.repopath= "%s/..." % self.repopath + f=os.popen('p4 -V 2>>%s'%logfile, 'rb') + a = f.readlines() + if f.close(): + raise + except: + die("Could not find the \"p4\" command") + + def p4(self, cmd, *args): + global logfile + cmd = "%s %s" % (cmd, ' '.join(args)) + report(2, "P4:", cmd) + f=os.popen('p4 -G %s 2>>%s' % (cmd,logfile), 'rb') + list = [] + while 1: + try: + list.append(marshal.load(f)) + except EOFError: + break + self.ret = f.close() + return list + + def sync(self, id, force=False, trick=False, test=False): + if force: + ret = self.p4("sync -f %s@%s"%(self.repopath, id))[0] + elif trick: + ret = self.p4("sync -k %s@%s"%(self.repopath, id))[0] + elif test: + ret = self.p4("sync -n %s@%s"%(self.repopath, id))[0] + else: + ret = self.p4("sync %s@%s"%(self.repopath, id))[0] + if ret['code'] == "error": + data = ret['data'].upper() + if data.find('VIEW') > 0: + die("Perforce reports %s is not in client view"% self.repopath) + elif data.find('UP-TO-DATE') < 0: + die("Could not sync files from perforce", self.repopath) + + def changes(self, since=0): + try: + list = [] + for rec in self.p4("changes %s@%s,#head" % (self.repopath, since+1)): + list.append(rec['change']) + list.reverse() + return list + except: + return [] + + def authors(self, filename): + f=open(filename) + for l in f.readlines(): + self.userlist[l[:l.find('=')].rstrip()] = \ + (l[l.find('=')+1:l.find('<')].rstrip(),l[l.find('<')+1:l.find('>')]) + f.close() + for f,e in self.userlist.items(): + report(2, f, ":", e[0], " <", e[1], ">") + + def _get_user(self, id): + if not self.userlist.has_key(id): + try: + user = self.p4("users", id)[0] + self.userlist[id] = (user['FullName'], user['Email']) + except: + self.userlist[id] = (id, "") + return self.userlist[id] + + def _format_date(self, ticks): + symbol='+' + name = time.tzname[0] + offset = time.timezone + if ticks[8]: + name = time.tzname[1] + offset = time.altzone + if offset < 0: + offset *= -1 + symbol = '-' + localo = "%s%02d%02d %s" % (symbol, offset / 3600, offset % 3600, name) + tickso = time.strftime("%a %b %d %H:%M:%S %Y", ticks) + return "%s %s" % (tickso, localo) + + def where(self): + try: + return self.p4("where %s" % self.repopath)[-1]['path'] + except: + return "" + + def describe(self, num): + desc = self.p4("describe -s", num)[0] + self.msg = desc['desc'] + self.author, self.email = self._get_user(desc['user']) + self.date = self._format_date(time.localtime(long(desc['time']))) + return self + +class git_command: + def __init__(self): + try: + self.version = self.git("--version")[0][12:].rstrip() + except: + die("Could not find the \"git\" command") + try: + self.gitdir = self.get_single("rev-parse --git-dir") + report(2, "gdir:", self.gitdir) + except: + die("Not a git repository... did you forget to \"git init\" ?") + try: + self.cdup = self.get_single("rev-parse --show-cdup") + if self.cdup != "": + os.chdir(self.cdup) + self.topdir = os.getcwd() + report(2, "topdir:", self.topdir) + except: + die("Could not find top git directory") + + def git(self, cmd): + global logfile + report(2, "GIT:", cmd) + f=os.popen('git %s 2>>%s' % (cmd,logfile), 'rb') + r=f.readlines() + self.ret = f.close() + return r + + def get_single(self, cmd): + return self.git(cmd)[0].rstrip() + + def current_branch(self): + try: + testit = self.git("rev-parse --verify HEAD")[0] + return self.git("symbolic-ref HEAD")[0][11:].rstrip() + except: + return None + + def get_config(self, variable): + try: + return self.git("config --get %s" % variable)[0].rstrip() + except: + return None + + def set_config(self, variable, value): + try: + self.git("config %s %s"%(variable, value) ) + except: + die("Could not set %s to " % variable, value) + + def make_tag(self, name, head): + self.git("tag -f %s %s"%(name,head)) + + def top_change(self, branch): + try: + a=self.get_single("name-rev --tags refs/heads/%s" % branch) + loc = a.find(' tags/') + 6 + if a[loc:loc+3] != "p4/": + raise + return int(a[loc+3:][:-2]) + except: + return 0 + + def update_index(self): + self.git("ls-files -m -d -o -z | git update-index --add --remove -z --stdin") + + def checkout(self, branch): + self.git("checkout %s" % branch) + + def repoint_head(self, branch): + self.git("symbolic-ref HEAD refs/heads/%s" % branch) + + def remove_files(self): + self.git("ls-files | xargs rm") + + def clean_directories(self): + self.git("clean -d") + + def fresh_branch(self, branch): + report(1, "Creating new branch", branch) + self.git("ls-files | xargs rm") + os.remove(".git/index") + self.repoint_head(branch) + self.git("clean -d") + + def basedir(self): + return self.topdir + + def commit(self, author, email, date, msg, id): + self.update_index() + fd=open(".msg", "w") + fd.writelines(msg) + fd.close() + try: + current = self.get_single("rev-parse --verify HEAD") + head = "-p HEAD" + except: + current = "" + head = "" + tree = self.get_single("write-tree") + for r,l in [('DATE',date),('NAME',author),('EMAIL',email)]: + os.environ['GIT_AUTHOR_%s'%r] = l + os.environ['GIT_COMMITTER_%s'%r] = l + commit = self.get_single("commit-tree %s %s < .msg" % (tree,head)) + os.remove(".msg") + self.make_tag("p4/%s"%id, commit) + self.git("update-ref HEAD %s %s" % (commit, current) ) + +try: + opts, args = getopt.getopt(sys.argv[1:], "qhvt:", + ["authors=","help","stitch=","timezone=","log=","ignore","notags"]) +except getopt.GetoptError: + usage() + +for o, a in opts: + if o == "-q": + verbosity = 0 + if o == "-v": + verbosity += 1 + if o in ("--log"): + logfile = a + if o in ("--notags"): + tagall = False + if o in ("-h", "--help"): + usage() + if o in ("--ignore"): + ignore_warnings = True + +git = git_command() +branch=git.current_branch() + +for o, a in opts: + if o in ("-t", "--timezone"): + git.set_config("perforce.timezone", a) + if o in ("--stitch"): + git.set_config("perforce.%s.path" % branch, a) + stitch = 1 + +if len(args) == 2: + branch = args[1] + git.checkout(branch) + if branch == git.current_branch(): + die("Branch %s already exists!" % branch) + report(1, "Setting perforce to ", args[0]) + git.set_config("perforce.%s.path" % branch, args[0]) +elif len(args) != 0: + die("You must specify the perforce //depot/path and git branch") + +p4path = git.get_config("perforce.%s.path" % branch) +if p4path == None: + die("Do not know Perforce //depot/path for git branch", branch) + +p4 = p4_command(p4path) + +for o, a in opts: + if o in ("-a", "--authors"): + p4.authors(a) + +localdir = git.basedir() +if p4.where()[:len(localdir)] != localdir: + report(1, "**WARNING** Appears p4 client is misconfigured") + report(1, " for sync from %s to %s" % (p4.repopath, localdir)) + if ignore_warnings != True: + die("Reconfigure or use \"--ignore\" on command line") + +if stitch == 0: + top = git.top_change(branch) +else: + top = 0 +changes = p4.changes(top) +count = len(changes) +if count == 0: + report(1, "Already up to date...") + sys.exit(0) + +ptz = git.get_config("perforce.timezone") +if ptz: + report(1, "Setting timezone to", ptz) + os.environ['TZ'] = ptz + time.tzset() + +if stitch == 1: + git.remove_files() + git.clean_directories() + p4.sync(changes[0], force=True) +elif top == 0 and branch != git.current_branch(): + p4.sync(changes[0], test=True) + report(1, "Creating new initial commit"); + git.fresh_branch(branch) + p4.sync(changes[0], force=True) +else: + p4.sync(changes[0], trick=True) + +report(1, "processing %s changes from p4 (%s) to git (%s)" % (count, p4.repopath, branch)) +for id in changes: + report(1, "Importing changeset", id) + change = p4.describe(id) + p4.sync(id) + if tagall : + git.commit(change.author, change.email, change.date, change.msg, id) + else: + git.commit(change.author, change.email, change.date, change.msg, "import") + if stitch == 1: + git.clean_directories() + stitch = 0 diff --git a/contrib/p4import/git-p4import.txt b/contrib/p4import/git-p4import.txt new file mode 100644 index 0000000000..9967587fe6 --- /dev/null +++ b/contrib/p4import/git-p4import.txt @@ -0,0 +1,167 @@ +git-p4import(1) +=============== + +NAME +---- +git-p4import - Import a Perforce repository into git + + +SYNOPSIS +-------- +[verse] +`git-p4import` [-q|-v] [--notags] [--authors ] [-t ] + +`git-p4import` --stitch +`git-p4import` + + +DESCRIPTION +----------- +Import a Perforce repository into an existing git repository. When +a and are specified a new branch with the +given name will be created and the initial import will begin. + +Once the initial import is complete you can do an incremental import +of new commits from the Perforce repository. You do this by checking +out the appropriate git branch and then running `git-p4import` without +any options. + +The standard p4 client is used to communicate with the Perforce +repository; it must be configured correctly in order for `git-p4import` +to operate (see below). + + +OPTIONS +------- +-q:: + Do not display any progress information. + +-v:: + Give extra progress information. + +\--authors:: + Specify an authors file containing a mapping of Perforce user + ids to full names and email addresses (see Notes below). + +\--notags:: + Do not create a tag for each imported commit. + +\--stitch:: + Import the contents of the given perforce branch into the + currently checked out git branch. + +\--log:: + Store debugging information in the specified file. + +-t:: + Specify that the remote repository is in the specified timezone. + Timezone must be in the format "US/Pacific" or "Europe/London" + etc. You only need to specify this once, it will be saved in + the git config file for the repository. + +:: + The Perforce path that will be imported into the specified branch. + +:: + The new branch that will be created to hold the Perforce imports. + + +P4 Client +--------- +You must make the `p4` client command available in your $PATH and +configure it to communicate with the target Perforce repository. +Typically this means you must set the "$P4PORT" and "$P4CLIENT" +environment variables. + +You must also configure a `p4` client "view" which maps the Perforce +branch into the top level of your git repository, for example: + +------------ +Client: myhost + +Root: /home/sean/import + +Options: noallwrite clobber nocompress unlocked modtime rmdir + +View: + //public/jam/... //myhost/jam/... +------------ + +With the above `p4` client setup, you could import the "jam" +perforce branch into a branch named "jammy", like so: + +------------ +$ mkdir -p /home/sean/import/jam +$ cd /home/sean/import/jam +$ git init +$ git p4import //public/jam jammy +------------ + + +Multiple Branches +----------------- +Note that by creating multiple "views" you can use `git-p4import` +to import additional branches into the same git repository. +However, the `p4` client has a limitation in that it silently +ignores all but the last "view" that maps into the same local +directory. So the following will *not* work: + +------------ +View: + //public/jam/... //myhost/jam/... + //public/other/... //myhost/jam/... + //public/guest/... //myhost/jam/... +------------ + +If you want more than one Perforce branch to be imported into the +same directory you must employ a workaround. A simple option is +to adjust your `p4` client before each import to only include a +single view. + +Another option is to create multiple symlinks locally which all +point to the same directory in your git repository and then use +one per "view" instead of listing the actual directory. + + +Tags +---- +A git tag of the form p4/xx is created for every change imported from +the Perforce repository where xx is the Perforce changeset number. +Therefore after the import you can use git to access any commit by its +Perforce number, e.g. git show p4/327. + +The tag associated with the HEAD commit is also how `git-p4import` +determines if there are new changes to incrementally import from the +Perforce repository. + +If you import from a repository with many thousands of changes +you will have an equal number of p4/xxxx git tags. Git tags can +be expensive in terms of disk space and repository operations. +If you don't need to perform further incremental imports, you +may delete the tags. + + +Notes +----- +You can interrupt the import (e.g. ctrl-c) at any time and restart it +without worry. + +Author information is automatically determined by querying the +Perforce "users" table using the id associated with each change. +However, if you want to manually supply these mappings you can do +so with the "--authors" option. It accepts a file containing a list +of mappings with each line containing one mapping in the format: + +------------ + perforce_id = Full Name +------------ + + +Author +------ +Written by Sean Estabrooks + + +GIT +--- +Part of the gitlink:git[7] suite -- cgit v1.2.3 From 99c01de402b543647a6500ceeaca7f62e343b144 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Sun, 15 Jul 2007 11:46:11 +0200 Subject: contrib/emacs/Makefile: Also install .el files. Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- contrib/emacs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile index 5e94d6fcd3..a48540a92b 100644 --- a/contrib/emacs/Makefile +++ b/contrib/emacs/Makefile @@ -13,7 +13,7 @@ all: $(ELC) install: all $(INSTALL) -d $(DESTDIR)$(emacsdir) - $(INSTALL_ELC) $(ELC) $(DESTDIR)$(emacsdir) + $(INSTALL_ELC) $(ELC:.elc=.el) $(ELC) $(DESTDIR)$(emacsdir) %.elc: %.el $(EMACS) -batch -f batch-byte-compile $< -- cgit v1.2.3 From 9f90c7335e223c26d19f3c01a6d89e6c0cd8b827 Mon Sep 17 00:00:00 2001 From: Scott Lamb Date: Sun, 15 Jul 2007 20:58:10 -0700 Subject: git-p4: use subprocess in p4CmdList This allows bidirectional piping - useful for "-x -" to avoid commandline arguments - and is a step toward bypassing the shell. Signed-off-by: Scott Lamb Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d877150f41..d93e656155 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -63,21 +63,34 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) -def p4CmdList(cmd): +def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: sys.stderr.write("Opening pipe: %s\n" % cmd) - pipe = os.popen(cmd, "rb") + + # Use a temporary file to avoid deadlocks without + # subprocess.communicate(), which would put another copy + # of stdout into memory. + stdin_file = None + if stdin is not None: + stdin_file = tempfile.TemporaryFile(prefix='p4-stdin', mode=stdin_mode) + stdin_file.write(stdin) + stdin_file.flush() + stdin_file.seek(0) + + p4 = subprocess.Popen(cmd, shell=True, + stdin=stdin_file, + stdout=subprocess.PIPE) result = [] try: while True: - entry = marshal.load(pipe) + entry = marshal.load(p4.stdout) result.append(entry) except EOFError: pass - exitCode = pipe.close() - if exitCode != None: + exitCode = p4.wait() + if exitCode != 0: entry = {} entry["p4ExitCode"] = exitCode result.append(entry) -- cgit v1.2.3 From 788001908c8960daa162c32baf3dc0f9ad1c16f8 Mon Sep 17 00:00:00 2001 From: Scott Lamb Date: Sun, 15 Jul 2007 20:58:11 -0700 Subject: git-p4: input to "p4 files" by stdin instead of arguments This approach, suggested by Alex Riesen, bypasses the need for xargs-style argument list handling. The handling in question looks broken in a corner case with SC_ARG_MAX=4096 and final argument over 96 characters. Signed-off-by: Scott Lamb Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d93e656155..54053e3f3d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -725,27 +725,13 @@ class P4Sync(Command): if not files: return - # We cannot put all the files on the command line - # OS have limitations on the max lenght of arguments - # POSIX says it's 4096 bytes, default for Linux seems to be 130 K. - # and all OS from the table below seems to be higher than POSIX. - # See http://www.in-ulm.de/~mascheck/various/argmax/ - if (self.isWindows): - argmax = 2000 - else: - argmax = min(4000, os.sysconf('SC_ARG_MAX')) - - chunk = '' - filedata = [] - for i in xrange(len(files)): - f = files[i] - chunk += '"%s#%s" ' % (f['path'], f['rev']) - if len(chunk) > argmax or i == len(files)-1: - data = p4CmdList('print %s' % chunk) - if "p4ExitCode" in data[0]: - die("Problems executing p4. Error: [%d]." % (data[0]['p4ExitCode'])); - filedata.extend(data) - chunk = '' + filedata = p4CmdList('-x - print', + stdin='\n'.join(['%s#%s' % (f['path'], f['rev']) + for f in files]), + stdin_mode='w+') + if "p4ExitCode" in filedata[0]: + die("Problems executing p4. Error: [%d]." + % (filedata[0]['p4ExitCode'])); j = 0; contents = {} -- cgit v1.2.3 From 062410bb9d6553e6bc4f8fa7f0cab1ed63b75628 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 18 Jul 2007 10:56:31 +0200 Subject: git-p4: Cleanup, make listExistingP4Branches a global function for later use. Signed-off-by: Simon Hausmann Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 54053e3f3d..d4a2f14311 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -181,6 +181,29 @@ def gitBranchExists(branch): def gitConfig(key): return read_pipe("git config %s" % key, ignore_error=True).strip() +def p4BranchesInGit(branchesAreInRemotes = True): + branches = {} + + cmdline = "git rev-parse --symbolic " + if branchesAreInRemotes: + cmdline += " --remotes" + else: + cmdline += " --branches" + + for line in read_pipe_lines(cmdline): + line = line.strip() + + ## only import to p4/ + if not line.startswith('p4/') or line == "p4/HEAD": + continue + branch = line + + # strip off p4 + branch = re.sub ("^p4/", "", line) + + branches[branch] = parseRevision(line) + return branches + def findUpstreamBranchPoint(head = "HEAD"): settings = None branchPoint = "" -- cgit v1.2.3 From 86506fe54c8e9e5f75a73956840497e3e5744f86 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 18 Jul 2007 12:40:12 +0200 Subject: git-p4: Fix upstream branch detection for submit/rebase with multiple branches. Don't use git name-rev to locate the upstream git-p4 branch for rebase and submit but instead locate the branch by comparing the depot paths. name-rev may produce results like wrongbranch~12 as it uses the first match. Signed-off-by: Simon Hausmann Signed-off-by: Marius Storm-Olsen --- contrib/fast-import/git-p4 | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d4a2f14311..a65f53a47b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -205,26 +205,31 @@ def p4BranchesInGit(branchesAreInRemotes = True): return branches def findUpstreamBranchPoint(head = "HEAD"): + branches = p4BranchesInGit() + # map from depot-path to branch name + branchByDepotPath = {} + for branch in branches.keys(): + tip = branches[branch] + log = extractLogMessageFromGitCommit(tip) + settings = extractSettingsGitLog(log) + if settings.has_key("depot-paths"): + paths = ",".join(settings["depot-paths"]) + branchByDepotPath[paths] = "remotes/p4/" + branch + settings = None - branchPoint = "" parent = 0 while parent < 65535: commit = head + "~%s" % parent log = extractLogMessageFromGitCommit(commit) settings = extractSettingsGitLog(log) - if not settings.has_key("depot-paths"): - parent = parent + 1 - continue - - names = read_pipe_lines("git name-rev \"--refs=refs/remotes/p4/*\" \"%s\"" % commit) - if len(names) <= 0: - continue + if settings.has_key("depot-paths"): + paths = ",".join(settings["depot-paths"]) + if branchByDepotPath.has_key(paths): + return [branchByDepotPath[paths], settings] - # strip away the beginning of 'HEAD~42 refs/remotes/p4/foo' - branchPoint = names[0].strip()[len(commit) + 1:] - break + parent = parent + 1 - return [branchPoint, settings] + return ["", settings] class Command: def __init__(self): -- cgit v1.2.3 From 144ff46b196e49fd52b2ecf0aaa1db4c190393b9 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 18 Jul 2007 17:27:50 +0200 Subject: git-p4: Cleanup, used common function for listing imported p4 branches Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a65f53a47b..e3404ca853 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1006,27 +1006,11 @@ class P4Sync(Command): self.knownBranches[branch] = branch def listExistingP4GitBranches(self): - self.p4BranchesInGit = [] - - cmdline = "git rev-parse --symbolic " - if self.importIntoRemotes: - cmdline += " --remotes" - else: - cmdline += " --branches" - - for line in read_pipe_lines(cmdline): - line = line.strip() - - ## only import to p4/ - if not line.startswith('p4/') or line == "p4/HEAD": - continue - branch = line - - # strip off p4 - branch = re.sub ("^p4/", "", line) - - self.p4BranchesInGit.append(branch) - self.initialParents[self.refPrefix + branch] = parseRevision(line) + # branches holds mapping from name to commit + branches = p4BranchesInGit(self.importIntoRemotes) + self.p4BranchesInGit = branches.keys() + for branch in branches.keys(): + self.initialParents[self.refPrefix + branch] = branches[branch] def createOrUpdateBranchesFromOrigin(self): if not self.silent: -- cgit v1.2.3 From 62e09ce998dd7f6b844deb650101c743a5c4ce50 Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Fri, 20 Jul 2007 01:42:28 +0200 Subject: Make git tag a builtin. This replaces the script "git-tag.sh" with "builtin-tag.c". The existing test suite for "git tag" guarantees the compatibility with the features provided by the script version. There are some minor changes in the behaviour of "git tag" here: "git tag -v" now can get more than one tag to verify, like "git tag -d" does, "git tag" with no arguments prints all tags, more like "git branch" does, and "git tag -n" also prints all tags with annotations (without needing -l). Tests and documentation were also updated to reflect these changes. The program is currently calling the script "git verify-tag" for verify. This can be changed porting it to C and calling its functions directly from builtin-tag.c. Signed-off-by: Carlos Rica Signed-off-by: Junio C Hamano --- contrib/examples/git-tag.sh | 205 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100755 contrib/examples/git-tag.sh (limited to 'contrib') diff --git a/contrib/examples/git-tag.sh b/contrib/examples/git-tag.sh new file mode 100755 index 0000000000..5ee3f50a3c --- /dev/null +++ b/contrib/examples/git-tag.sh @@ -0,0 +1,205 @@ +#!/bin/sh +# Copyright (c) 2005 Linus Torvalds + +USAGE='[-n []] -l [] | [-a | -s | -u ] [-f | -d | -v] [-m ] []' +SUBDIRECTORY_OK='Yes' +. git-sh-setup + +message_given= +annotate= +signed= +force= +message= +username= +list= +verify= +LINES=0 +while case "$#" in 0) break ;; esac +do + case "$1" in + -a) + annotate=1 + shift + ;; + -s) + annotate=1 + signed=1 + shift + ;; + -f) + force=1 + shift + ;; + -n) + case "$#,$2" in + 1,* | *,-*) + LINES=1 # no argument + ;; + *) shift + LINES=$(expr "$1" : '\([0-9]*\)') + [ -z "$LINES" ] && LINES=1 # 1 line is default when -n is used + ;; + esac + shift + ;; + -l) + list=1 + shift + case $# in + 0) PATTERN= + ;; + *) + PATTERN="$1" # select tags by shell pattern, not re + shift + ;; + esac + git rev-parse --symbolic --tags | sort | + while read TAG + do + case "$TAG" in + *$PATTERN*) ;; + *) continue ;; + esac + [ "$LINES" -le 0 ] && { echo "$TAG"; continue ;} + OBJTYPE=$(git cat-file -t "$TAG") + case $OBJTYPE in + tag) + ANNOTATION=$(git cat-file tag "$TAG" | + sed -e '1,/^$/d' | + sed -n -e " + /^-----BEGIN PGP SIGNATURE-----\$/q + 2,\$s/^/ / + p + ${LINES}q + ") + printf "%-15s %s\n" "$TAG" "$ANNOTATION" + ;; + *) echo "$TAG" + ;; + esac + done + ;; + -m) + annotate=1 + shift + message="$1" + if test "$#" = "0"; then + die "error: option -m needs an argument" + else + message="$1" + message_given=1 + shift + fi + ;; + -F) + annotate=1 + shift + if test "$#" = "0"; then + die "error: option -F needs an argument" + else + message="$(cat "$1")" + message_given=1 + shift + fi + ;; + -u) + annotate=1 + signed=1 + shift + if test "$#" = "0"; then + die "error: option -u needs an argument" + else + username="$1" + shift + fi + ;; + -d) + shift + had_error=0 + for tag + do + cur=$(git show-ref --verify --hash -- "refs/tags/$tag") || { + echo >&2 "Seriously, what tag are you talking about?" + had_error=1 + continue + } + git update-ref -m 'tag: delete' -d "refs/tags/$tag" "$cur" || { + had_error=1 + continue + } + echo "Deleted tag $tag." + done + exit $had_error + ;; + -v) + shift + tag_name="$1" + tag=$(git show-ref --verify --hash -- "refs/tags/$tag_name") || + die "Seriously, what tag are you talking about?" + git-verify-tag -v "$tag" + exit $? + ;; + -*) + usage + ;; + *) + break + ;; + esac +done + +[ -n "$list" ] && exit 0 + +name="$1" +[ "$name" ] || usage +prev=0000000000000000000000000000000000000000 +if git show-ref --verify --quiet -- "refs/tags/$name" +then + test -n "$force" || die "tag '$name' already exists" + prev=`git rev-parse "refs/tags/$name"` +fi +shift +git check-ref-format "tags/$name" || + die "we do not like '$name' as a tag name." + +object=$(git rev-parse --verify --default HEAD "$@") || exit 1 +type=$(git cat-file -t $object) || exit 1 +tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1 + +test -n "$username" || + username=$(git repo-config user.signingkey) || + username=$(expr "z$tagger" : 'z\(.*>\)') + +trap 'rm -f "$GIT_DIR"/TAG_TMP* "$GIT_DIR"/TAG_FINALMSG "$GIT_DIR"/TAG_EDITMSG' 0 + +if [ "$annotate" ]; then + if [ -z "$message_given" ]; then + ( echo "#" + echo "# Write a tag message" + echo "#" ) > "$GIT_DIR"/TAG_EDITMSG + git_editor "$GIT_DIR"/TAG_EDITMSG || exit + else + printf '%s\n' "$message" >"$GIT_DIR"/TAG_EDITMSG + fi + + grep -v '^#' <"$GIT_DIR"/TAG_EDITMSG | + git stripspace >"$GIT_DIR"/TAG_FINALMSG + + [ -s "$GIT_DIR"/TAG_FINALMSG -o -n "$message_given" ] || { + echo >&2 "No tag message?" + exit 1 + } + + ( printf 'object %s\ntype %s\ntag %s\ntagger %s\n\n' \ + "$object" "$type" "$name" "$tagger"; + cat "$GIT_DIR"/TAG_FINALMSG ) >"$GIT_DIR"/TAG_TMP + rm -f "$GIT_DIR"/TAG_TMP.asc "$GIT_DIR"/TAG_FINALMSG + if [ "$signed" ]; then + gpg -bsa -u "$username" "$GIT_DIR"/TAG_TMP && + cat "$GIT_DIR"/TAG_TMP.asc >>"$GIT_DIR"/TAG_TMP || + die "failed to sign the tag with GPG." + fi + object=$(git-mktag < "$GIT_DIR"/TAG_TMP) +fi + +git update-ref "refs/tags/$name" "$object" "$prev" -- cgit v1.2.3 From 93c22eeb30d2f43fc1e7a397e71f9bf8fb767962 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 24 Jul 2007 12:12:47 +0200 Subject: git.el: Support for incremental status updates. When we know which files have been modified, we can now run diff-index or ls-files with a file list to refresh only the specified files instead of the whole project. This also allows proper refreshing of files upon add/delete/resolve, instead of making assumptions about the new file state. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 224 ++++++++++++++++++++++++++------------------------- 1 file changed, 113 insertions(+), 111 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 457f95fc05..b92bbe8728 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -314,8 +314,8 @@ and returns the process output as a string." (sort-lines nil (point-min) (point-max)) (save-buffer)) (when created - (git-run-command nil nil "update-index" "--info-only" "--add" "--" (file-relative-name ignore-name))) - (git-add-status-file (if created 'added 'modified) (file-relative-name ignore-name)))) + (git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) + (git-update-status-files (list (file-relative-name ignore-name)) 'unknown))) ; propertize definition for XEmacs, stolen from erc-compat (eval-when-compile @@ -523,23 +523,39 @@ and returns the process output as a string." " " (git-escape-file-name (git-fileinfo->name info)) (git-rename-as-string info)))) -(defun git-parse-status (status) - "Parse the output of git-diff-index in the current buffer." - (goto-char (point-min)) - (while (re-search-forward - ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" - nil t 1) - (let ((old-perm (string-to-number (match-string 1) 8)) - (new-perm (string-to-number (match-string 2) 8)) - (state (or (match-string 4) (match-string 6))) - (name (or (match-string 5) (match-string 7))) - (new-name (match-string 8))) - (if new-name ; copy or rename - (if (eq ?C (string-to-char state)) - (ewoc-enter-last status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name)) - (ewoc-enter-last status (git-create-fileinfo 'deleted name 0 0 'rename new-name)) - (ewoc-enter-last status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name))) - (ewoc-enter-last status (git-create-fileinfo (git-state-code state) name old-perm new-perm)))))) +(defun git-insert-fileinfo (status info &optional refresh) + "Insert INFO in the status buffer, optionally refreshing an existing one." + (let ((node (and refresh + (git-find-status-file status (git-fileinfo->name info))))) + (setf (git-fileinfo->needs-refresh info) t) + (when node ;preserve the marked flag + (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))) + (if node (ewoc-set-data node info) (ewoc-enter-last status info)))) + +(defun git-run-diff-index (status files) + "Run git-diff-index on FILES and parse the results into STATUS. +Return the list of files that haven't been handled." + (let ((refresh files)) + (with-temp-buffer + (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files) + (goto-char (point-min)) + (while (re-search-forward + ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" + nil t 1) + (let ((old-perm (string-to-number (match-string 1) 8)) + (new-perm (string-to-number (match-string 2) 8)) + (state (or (match-string 4) (match-string 6))) + (name (or (match-string 5) (match-string 7))) + (new-name (match-string 8))) + (if new-name ; copy or rename + (if (eq ?C (string-to-char state)) + (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) refresh) + (git-insert-fileinfo status (git-create-fileinfo 'deleted name 0 0 'rename new-name) refresh) + (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)) refresh) + (git-insert-fileinfo status (git-create-fileinfo (git-state-code state) name old-perm new-perm) refresh)) + (setq files (delete name files)) + (when new-name (setq files (delete new-name files))))))) + files) (defun git-find-status-file (status file) "Find a given file in the status ewoc and return its node." @@ -548,32 +564,59 @@ and returns the process output as a string." (setq node (ewoc-next status node))) node)) -(defun git-parse-ls-files (status default-state &optional skip-existing) - "Parse the output of git-ls-files in the current buffer." - (goto-char (point-min)) - (let (infolist) - (while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1) - (let ((state (match-string 1)) - (name (match-string 2))) - (unless (and skip-existing (git-find-status-file status name)) - (push (git-create-fileinfo (or (git-state-code state) default-state) name) infolist)))) - (dolist (info (nreverse infolist)) - (ewoc-enter-last status info)))) - -(defun git-parse-ls-unmerged (status) - "Parse the output of git-ls-files -u in the current buffer." - (goto-char (point-min)) - (let (files) - (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) - (let ((node (git-find-status-file status (match-string 1)))) - (when node (push (ewoc-data node) files)))) - (git-set-files-state files 'unmerged))) - -(defun git-add-status-file (state name) - "Add a new file to the status list (if not existing already) and return its node." +(defun git-run-ls-files (status files default-state &rest options) + "Run git-ls-files on FILES and parse the results into STATUS. +Return the list of files that haven't been handled." + (let ((refresh files)) + (with-temp-buffer + (apply #'git-run-command t nil "ls-files" "-z" "-t" (append options (list "--") files)) + (goto-char (point-min)) + (while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1) + (let ((state (match-string 1)) + (name (match-string 2))) + (git-insert-fileinfo status (git-create-fileinfo (or (git-state-code state) default-state) name) refresh) + (setq files (delete name files)))))) + files) + +(defun git-run-ls-unmerged (status files) + "Run git-ls-files -u on FILES and parse the results into STATUS." + (with-temp-buffer + (apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files) + (goto-char (point-min)) + (let (unmerged-files) + (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) + (let ((node (git-find-status-file status (match-string 1)))) + (when node (push (ewoc-data node) unmerged-files)))) + (git-set-files-state unmerged-files 'unmerged)))) + +(defun git-update-status-files (files &optional default-state) + "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) - (or (git-find-status-file git-status name) - (ewoc-enter-last git-status (git-create-fileinfo state name)))) + (let* ((status git-status) + (remaining-files + (if (git-empty-db-p) ; we need some special handling for an empty db + (git-run-ls-files status files 'added "-c") + (git-run-diff-index status files)))) + (git-run-ls-unmerged status files) + (when (and (or (not files) remaining-files) + (file-readable-p ".git/info/exclude")) + (setq remaining-files (git-run-ls-files status remaining-files + 'unknown "-o" "--exclude-from=.git/info/exclude" + (concat "--exclude-per-directory=" git-per-dir-ignore-file)))) + ; mark remaining files with the default state (or remove them if nil) + (when remaining-files + (if default-state + (ewoc-map (lambda (info) + (when (member (git-fileinfo->name info) remaining-files) + (git-set-files-state (list info) default-state)) + nil) + status) + (ewoc-filter status + (lambda (info files) + (not (member (git-fileinfo->name info) files))) + remaining-files))) + (git-refresh-files) + (git-refresh-ewoc-hf status))) (defun git-marked-files () "Return a list of all marked files, or if none a list containing just the file at cursor position." @@ -789,54 +832,34 @@ and returns the process output as a string." (defun git-add-file () "Add marked file(s) to the index cache." (interactive) - (let ((files (git-marked-files-state 'unknown))) + (let ((files (git-get-filenames (git-marked-files-state 'unknown)))) (unless files - (push (ewoc-data - (git-add-status-file 'added (file-relative-name - (read-file-name "File to add: " nil nil t)))) - files)) - (apply #'git-run-command nil nil "update-index" "--info-only" "--add" "--" (git-get-filenames files)) - (git-set-files-state files 'added) - (git-refresh-files))) + (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) + (apply #'git-run-command nil nil "update-index" "--add" "--" files) + (git-update-status-files files 'uptodate))) (defun git-ignore-file () "Add marked file(s) to the ignore list." (interactive) - (let ((files (git-marked-files-state 'unknown))) + (let ((files (git-get-filenames (git-marked-files-state 'unknown)))) (unless files - (push (ewoc-data - (git-add-status-file 'unknown (file-relative-name - (read-file-name "File to ignore: " nil nil t)))) - files)) - (dolist (info files) (git-append-to-ignore (git-fileinfo->name info))) - (git-set-files-state files 'ignored) - (git-refresh-files))) + (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files)) + (dolist (f files) (git-append-to-ignore f)) + (git-update-status-files files 'ignored))) (defun git-remove-file () "Remove the marked file(s)." (interactive) - (let ((files (git-marked-files-state 'added 'modified 'unknown 'uptodate))) + (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate)))) (unless files - (push (ewoc-data - (git-add-status-file 'unknown (file-relative-name - (read-file-name "File to remove: " nil nil t)))) - files)) + (push (file-relative-name (read-file-name "File to remove: " nil nil t)) files)) (if (yes-or-no-p (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" ""))) (progn - (dolist (info files) - (let ((name (git-fileinfo->name info))) - (when (file-exists-p name) (delete-file name)))) - (apply #'git-run-command nil nil "update-index" "--info-only" "--remove" "--" (git-get-filenames files)) - ; remove unknown files from the list, set the others to deleted - (ewoc-filter git-status - (lambda (info files) - (not (and (memq info files) (eq (git-fileinfo->state info) 'unknown)))) - files) - (git-set-files-state files 'deleted) - (git-refresh-files) - (unless (ewoc-nth git-status 0) ; refresh header if list is empty - (git-refresh-ewoc-hf git-status))) + (dolist (name files) + (when (file-exists-p name) (delete-file name))) + (apply #'git-run-command nil nil "update-index" "--remove" "--" files) + (git-update-status-files files nil)) (message "Aborting")))) (defun git-revert-file () @@ -849,26 +872,23 @@ and returns the process output as a string." (format "Revert %d file%s? " (length files) (if (> (length files) 1) "s" "")))) (dolist (info files) (case (git-fileinfo->state info) - ('added (push info added)) - ('deleted (push info modified)) - ('unmerged (push info modified)) - ('modified (push info modified)))) + ('added (push (git-fileinfo->name info) added)) + ('deleted (push (git-fileinfo->name info) modified)) + ('unmerged (push (git-fileinfo->name info) modified)) + ('modified (push (git-fileinfo->name info) modified)))) (when added - (apply #'git-run-command nil nil "update-index" "--force-remove" "--" (git-get-filenames added)) - (git-set-files-state added 'unknown)) + (apply #'git-run-command nil nil "update-index" "--force-remove" "--" added)) (when modified - (apply #'git-run-command nil nil "checkout" "HEAD" (git-get-filenames modified)) - (git-set-files-state modified 'uptodate)) - (git-refresh-files)))) + (apply #'git-run-command nil nil "checkout" "HEAD" modified)) + (git-update-status-files (append added modified) 'uptodate)))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." (interactive) - (let ((files (git-marked-files-state 'unmerged))) + (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) (when files - (apply #'git-run-command nil nil "update-index" "--" (git-get-filenames files)) - (git-set-files-state files 'modified) - (git-refresh-files)))) + (apply #'git-run-command nil nil "update-index" "--" files) + (git-update-status-files files 'uptodate)))) (defun git-remove-handled () "Remove handled files from the status list." @@ -1071,27 +1091,9 @@ and returns the process output as a string." (pos (ewoc-locate status)) (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) (unless status (error "Not in git-status buffer.")) + (git-run-command nil nil "update-index" "--refresh") (git-clear-status status) - (git-run-command nil nil "update-index" "--info-only" "--refresh") - (if (git-empty-db-p) - ; we need some special handling for an empty db - (with-temp-buffer - (git-run-command t nil "ls-files" "-z" "-t" "-c") - (git-parse-ls-files status 'added)) - (with-temp-buffer - (git-run-command t nil "diff-index" "-z" "-M" "HEAD") - (git-parse-status status))) - (with-temp-buffer - (git-run-command t nil "ls-files" "-z" "-u") - (git-parse-ls-unmerged status)) - (when (file-readable-p ".git/info/exclude") - (with-temp-buffer - (git-run-command t nil "ls-files" "-z" "-t" "-o" - "--exclude-from=.git/info/exclude" - (concat "--exclude-per-directory=" git-per-dir-ignore-file)) - (git-parse-ls-files status 'unknown))) - (git-refresh-files) - (git-refresh-ewoc-hf status) + (git-update-status-files nil) ; move point to the current file name if any (let ((node (and cur-name (git-find-status-file status cur-name)))) (when node (ewoc-goto-node status node))))) -- cgit v1.2.3 From ceefa44fe2d72f73548c4c36b42916b9c60ae16b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 24 Jul 2007 12:02:28 +0200 Subject: git.el: Pass an explicit argument to enable smerge-mode. Without argument the mode is toggled, which would do the wrong thing if the file was already open. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index b92bbe8728..53dd703260 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1058,7 +1058,7 @@ Return the list of files that haven't been handled." (let ((info (ewoc-data (ewoc-locate git-status)))) (find-file (git-fileinfo->name info)) (when (eq 'unmerged (git-fileinfo->state info)) - (smerge-mode)))) + (smerge-mode 1)))) (defun git-find-file-other-window () "Visit the current file in its own buffer in another window." -- cgit v1.2.3 From b2d2d16af7ff686fb96d344daaf49653fd67366a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 25 Jul 2007 09:31:38 +0200 Subject: git-p4: Fix p4 user cache population on Windows. Fall back to USERPROFILE if HOME isn't set. Signed-off-by: Simon Hausmann Signed-off-by: Marius Storm-Olsen Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e3404ca853..1f5a56ee7f 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -901,7 +901,8 @@ class P4Sync(Command): % (labelDetails["label"], change)) def getUserCacheFilename(self): - return os.environ["HOME"] + "/.gitp4-usercache.txt" + home = os.environ.get("HOME", os.environ.get("USERPROFILE")) + return home + "/.gitp4-usercache.txt" def getUserMapFromPerforceServer(self): if self.userMapFromPerforceServer: -- cgit v1.2.3 From 2ae68fcb785a617793813abcea19893e13e436b0 Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Fri, 27 Jul 2007 06:07:34 +0200 Subject: Make verify-tag a builtin. This replaces "git-verify-tag.sh" with "builtin-verify-tag.c". Testing relies on the "git tag -v" tests calling this command. A temporary file is needed when calling to gpg, because git is already creating detached signatures (gpg option -b) to sign tags (instead of leaving gpg to add the signature to the file by itself), and those signatures need to be supplied in a separate file to be verified by gpg. The program uses git_mkstemp to create that temporary file needed by gpg, instead of the previously used "$GIT_DIR/.tmp-vtag", in order to allow the command to be used in read-only repositories, and also prevent other instances of git to read or remove the same file. Signal SIGPIPE is ignored because the program sometimes was terminated because that signal when writing the input for gpg. The command now can receive many tag names to be verified. Documentation is also updated here to reflect this new behaviour. Signed-off-by: Carlos Rica Signed-off-by: Junio C Hamano --- contrib/examples/git-verify-tag.sh | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100755 contrib/examples/git-verify-tag.sh (limited to 'contrib') diff --git a/contrib/examples/git-verify-tag.sh b/contrib/examples/git-verify-tag.sh new file mode 100755 index 0000000000..37b0023b27 --- /dev/null +++ b/contrib/examples/git-verify-tag.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +USAGE='' +SUBDIRECTORY_OK='Yes' +. git-sh-setup + +verbose= +while case $# in 0) break;; esac +do + case "$1" in + -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) + verbose=t ;; + *) + break ;; + esac + shift +done + +if [ "$#" != "1" ] +then + usage +fi + +type="$(git cat-file -t "$1" 2>/dev/null)" || + die "$1: no such object." + +test "$type" = tag || + die "$1: cannot verify a non-tag object of type $type." + +case "$verbose" in +t) + git cat-file -p "$1" | + sed -n -e '/^-----BEGIN PGP SIGNATURE-----/q' -e p + ;; +esac + +trap 'rm -f "$GIT_DIR/.tmp-vtag"' 0 + +git cat-file tag "$1" >"$GIT_DIR/.tmp-vtag" || exit 1 +sed -n -e ' + /^-----BEGIN PGP SIGNATURE-----$/q + p +' <"$GIT_DIR/.tmp-vtag" | +gpg --verify "$GIT_DIR/.tmp-vtag" - || exit 1 +rm -f "$GIT_DIR/.tmp-vtag" -- cgit v1.2.3 From 61988f1127587f8597b8b41da78a65717851e1fa Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 31 Jul 2007 23:03:41 -0700 Subject: git.el: Avoid using ewoc-set-data for compatibility with Emacs 21. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexandre Julliard Acked-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 53dd703260..7470f13185 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -530,7 +530,7 @@ and returns the process output as a string." (setf (git-fileinfo->needs-refresh info) t) (when node ;preserve the marked flag (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))) - (if node (ewoc-set-data node info) (ewoc-enter-last status info)))) + (if node (setf (ewoc-data node) info) (ewoc-enter-last status info)))) (defun git-run-diff-index (status files) "Run git-diff-index on FILES and parse the results into STATUS. -- cgit v1.2.3 From 274e13e0e9c7e475bbc342a10d614dd40b0e4c15 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 31 Jul 2007 12:36:32 +0200 Subject: git.el: Take into account the core.excludesfile config option. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also don't require .git/info/exclude to exist in order to list unknown files. Signed-off-by: Alexandre Julliard Acked-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 7470f13185..f6102fc344 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -589,6 +589,16 @@ Return the list of files that haven't been handled." (when node (push (ewoc-data node) unmerged-files)))) (git-set-files-state unmerged-files 'unmerged)))) +(defun git-get-exclude-files () + "Get the list of exclude files to pass to git-ls-files." + (let (files + (config (git-config "core.excludesfile"))) + (when (file-readable-p ".git/info/exclude") + (push ".git/info/exclude" files)) + (when (and config (file-readable-p config)) + (push config files)) + files)) + (defun git-update-status-files (files &optional default-state) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) @@ -598,11 +608,11 @@ Return the list of files that haven't been handled." (git-run-ls-files status files 'added "-c") (git-run-diff-index status files)))) (git-run-ls-unmerged status files) - (when (and (or (not files) remaining-files) - (file-readable-p ".git/info/exclude")) - (setq remaining-files (git-run-ls-files status remaining-files - 'unknown "-o" "--exclude-from=.git/info/exclude" - (concat "--exclude-per-directory=" git-per-dir-ignore-file)))) + (when (or (not files) remaining-files) + (let ((exclude-files (git-get-exclude-files))) + (setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o" + (concat "--exclude-per-directory=" git-per-dir-ignore-file) + (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) ; mark remaining files with the default state (or remove them if nil) (when remaining-files (if default-state -- cgit v1.2.3 From a4eba020f96e1b85f856a992782b8bb2bab6a719 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Mon, 23 Jul 2007 15:51:49 -0700 Subject: Sort output of "p4 change" in incremental import before further processing P4 change outputs the changes sorted for each directory separately. We want the global ordering on the changes, hence we sort. Signed-off-by: Han-Wen Nienhuys Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 1f5a56ee7f..f00c691a7b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1296,7 +1296,7 @@ class P4Sync(Command): changeNum = line.split(" ")[1] changes.append(changeNum) - changes.reverse() + changes.sort() if len(self.maxChanges) > 0: changes = changes[0:min(int(self.maxChanges), len(changes))] -- cgit v1.2.3 From 7fcff9def564fae7de5d2a34a095229a999b80b0 Mon Sep 17 00:00:00 2001 From: Han-Wen Nienhuys Date: Mon, 23 Jul 2007 15:56:37 -0700 Subject: Fix style nit in Python slicing. Python slices start at 0 by default. Signed-off-by: Han-Wen Nienhuys Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f00c691a7b..41e86e76cb 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1181,11 +1181,11 @@ class P4Sync(Command): elif ',' not in self.changeRange: self.revision = self.changeRange self.changeRange = "" - p = p[0:atIdx] + p = p[:atIdx] elif p.find("#") != -1: hashIdx = p.index("#") self.revision = p[hashIdx:] - p = p[0:hashIdx] + p = p[:hashIdx] elif self.previousDepotPaths == []: self.revision = "#head" @@ -1299,7 +1299,7 @@ class P4Sync(Command): changes.sort() if len(self.maxChanges) > 0: - changes = changes[0:min(int(self.maxChanges), len(changes))] + changes = changes[:min(int(self.maxChanges), len(changes))] if len(changes) == 0: if not self.silent: -- cgit v1.2.3 From 7fd53fce1c574f6a4940eedf36383a4e9ed7ae6a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 3 Aug 2007 02:04:37 -0700 Subject: git-completion: add "git stash" This is a new addition to 1.5.3; let's teach it to the completion before the final release. [sp: Added missing git-stash completion configuration] Signed-off-by: Junio C Hamano Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f2b10fa5f6..82b9ed40d8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -972,6 +972,11 @@ _git_show () __git_complete_file } +_git_stash () +{ + __gitcomp 'list show apply clear' +} + _git () { local i c=1 command __git_dir @@ -1028,6 +1033,7 @@ _git () shortlog) _git_shortlog ;; show) _git_show ;; show-branch) _git_log ;; + stash) _git_stash ;; whatchanged) _git_log ;; *) COMPREPLY=() ;; esac @@ -1073,6 +1079,7 @@ complete -o default -o nospace -F _git_remote git-remote complete -o default -o nospace -F _git_reset git-reset complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show +complete -o default -o nospace -F _git_stash git-stash complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged -- cgit v1.2.3 From 2ec39edad9c3be74b5fed5ab5c122493e6a53c66 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 3 Aug 2007 20:19:09 -0700 Subject: INSTALL: add warning on docbook-xsl 1.72 and 1.73 Signed-off-by: Junio C Hamano --- contrib/patches/docbook-xsl-manpages-charmap.patch | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 contrib/patches/docbook-xsl-manpages-charmap.patch (limited to 'contrib') diff --git a/contrib/patches/docbook-xsl-manpages-charmap.patch b/contrib/patches/docbook-xsl-manpages-charmap.patch new file mode 100644 index 0000000000..f2b08b4f4a --- /dev/null +++ b/contrib/patches/docbook-xsl-manpages-charmap.patch @@ -0,0 +1,21 @@ +From: Ismail Dönmez + +Trying to build the documentation with docbook-xsl 1.73 may result in +the following error. This patch fixes it. + +$ xmlto -m callouts.xsl man git-add.xml +runtime error: file +file:///usr/share/sgml/docbook/xsl-stylesheets-1.73.0/manpages/other.xsl line +129 element call-template +The called template 'read-character-map' was not found. + +--- docbook-xsl-1.73.0/manpages/docbook.xsl.manpages-charmap 2007-07-23 16:24:23.000000000 +0100 ++++ docbook-xsl-1.73.0/manpages/docbook.xsl 2007-07-23 16:25:16.000000000 +0100 +@@ -37,6 +37,7 @@ + + + ++ + + + -- cgit v1.2.3 From 74276ec6f2d2123714066cee24b26a629930e7a8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 7 Aug 2007 12:28:00 +0200 Subject: git-p4: Fix support for symlinks. Detect symlinks as file type, set the git file mode accordingly and strip off the trailing newline in the p4 print output. Make the mode handling a bit more readable at the same time. Signed-off-by: Simon Hausmann Acked-by: Brian Swetland Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 41e86e76cb..3cbb2da221 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -839,16 +839,20 @@ class P4Sync(Command): if file["action"] == "delete": self.gitStream.write("D %s\n" % relPath) else: - mode = 644 - if file["type"].startswith("x"): - mode = 755 - data = file['data'] + mode = "644" + if file["type"].startswith("x"): + mode = "755" + elif file["type"] == "symlink": + mode = "120000" + # p4 print on a symlink contains "target\n", so strip it off + data = data[:-1] + if self.isWindows and file["type"].endswith("text"): data = data.replace("\r\n", "\n") - self.gitStream.write("M %d inline %s\n" % (mode, relPath)) + self.gitStream.write("M %s inline %s\n" % (mode, relPath)) self.gitStream.write("data %s\n" % len(data)) self.gitStream.write(data) self.gitStream.write("\n") -- cgit v1.2.3 From ea99c3ae0e2cecaa0950532385319d60c59e97e0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 8 Aug 2007 17:06:55 +0200 Subject: git-p4: Fix git-p4 submit to include only changed files in the perforce submit template. Parse the files section in the "p4 change -o" output and remove lines with file changes in unrelated depot paths. Signed-off-by: Simon Hausmann Signed-off-by: Marius Storm-Olsen Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3cbb2da221..805d632a68 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -390,6 +390,30 @@ class P4Submit(Command): return result + def prepareSubmitTemplate(self): + # remove lines in the Files section that show changes to files outside the depot path we're committing into + template = "" + inFilesSection = False + for line in read_pipe_lines("p4 change -o"): + if inFilesSection: + if line.startswith("\t"): + # path starts and ends with a tab + path = line[1:] + lastTab = path.rfind("\t") + if lastTab != -1: + path = path[:lastTab] + if not path.startswith(self.depotPath): + continue + else: + inFilesSection = False + else: + if line.startswith("Files:"): + inFilesSection = True + + template += line + + return template + def applyCommit(self, id): if self.directSubmit: print "Applying local change in working directory/index" @@ -467,7 +491,7 @@ class P4Submit(Command): logMessage = logMessage.replace("\n", "\r\n") logMessage = logMessage.strip() - template = read_pipe("p4 change -o") + template = self.prepareSubmitTemplate() if self.interactive: submitTemplate = self.prepareLogMessage(template, logMessage) @@ -558,24 +582,24 @@ class P4Submit(Command): return False [upstream, settings] = findUpstreamBranchPoint() - depotPath = settings['depot-paths'][0] + self.depotPath = settings['depot-paths'][0] if len(self.origin) == 0: self.origin = upstream if self.verbose: print "Origin branch is " + self.origin - if len(depotPath) == 0: + if len(self.depotPath) == 0: print "Internal error: cannot locate perforce depot path from existing branches" sys.exit(128) - self.clientPath = p4Where(depotPath) + self.clientPath = p4Where(self.depotPath) if len(self.clientPath) == 0: - print "Error: Cannot locate perforce checkout of %s in client view" % depotPath + print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath sys.exit(128) - print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath) + print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath) self.oldWorkingDirectory = os.getcwd() if self.directSubmit: -- cgit v1.2.3 From b767c792fa202539cfb9bba36f46c62bcbf7c987 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 9 Aug 2007 02:38:09 -0400 Subject: Teach update-paranoid how to store ACLs organized by groups In some applications of this paranoid update hook the set of ACL rules that need to be applied to a user can be large, and the number of users that those rules must also be applied to can be more than a handful of individuals. Rather than repeating the same rules multiple times (once for each user) we now allow users to be members of groups, where the group supplies the list of ACL rules. For various reasons we don't depend on the underlying OS groups and instead perform our own group handling. Users can be made a member of one or more groups by setting the user.memberOf property within the "users/$who.acl" file: [user] memberOf = developer memberOf = administrator This will cause the hook to also parse the "groups/$groupname.acl" file for each value of user.memberOf, and merge any allow rules that match the current repository with the user's own private rules (if they had any). Since some rules are basically the same but may have a component differ based on the individual user, any user.* key may be inserted into a rule using the "${user.foo}" syntax. The allow rule does not match if the user does not define one (and exactly one) value for the key "foo". Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 60 +++++++++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 16 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 5ee1835c80..fb2aca3628 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -118,22 +118,29 @@ 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); +sub git_value (@) { + open(T,'-|','git',@_); local $_ = ; chop; close T; $_; +} + +sub parse_config ($$$$) { + my $data = shift; + local $ENV{GIT_DIR} = shift; + my $br = shift; + my $fn = shift; + info "Loading $br:$fn"; + open(I,'-|','git','cat-file','blob',"$br:$fn"); my $section = ''; while () { chomp; if (/^\s*$/ || /^\s*#/) { } elsif (/^\[([a-z]+)\]$/i) { - $section = $1; + $section = lc $1; } elsif (/^\[([a-z]+)\s+"(.*)"\]$/i) { - $section = "$1.$2"; + $section = join('.',lc $1,$2); } elsif (/^\s*([a-z][a-z0-9]+)\s*=\s*(.*?)\s*$/i) { - push @{$data->{"$section.$1"}}, $2; + push @{$data->{join('.',$section,lc $1)}}, $2; } else { - deny "bad config file line $. in $fn"; + deny "bad config file line $. in $br:$fn"; } } close I; @@ -202,11 +209,6 @@ sub check_committers (@) { } } -sub git_value (@) { - open(T,'-|','git',@_); local $_ = ; 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/,,; @@ -231,13 +233,39 @@ $op = 'U' if ($op eq 'R' && $ref =~ m,^heads/, && $old eq git_value('merge-base',$old,$new)); -# Load the user's ACL file. +# Load the user's ACL file. Expand groups (user.memberof) one level. { my %data = ('user.committer' => []); - parse_config(\%data, "$acl_branch:users/$this_user.acl"); + parse_config(\%data,$acl_git,$acl_branch,"external/$repository_name.acl"); + + %data = ( + 'user.committer' => $data{'user.committer'}, + 'user.memberof' => [], + ); + parse_config(\%data,$acl_git,$acl_branch,"users/$this_user.acl"); + %user_committer = map {$_ => $_} @{$data{'user.committer'}}; - my $rules = $data{"repository.$repository_name.allow"} || []; + my $rule_key = "repository.$repository_name.allow"; + my $rules = $data{$rule_key} || []; + + foreach my $group (@{$data{'user.memberof'}}) { + my %g; + parse_config(\%g,$acl_git,$acl_branch,"groups/$group.acl"); + my $group_rules = $g{$rule_key}; + push @$rules, @$group_rules if $group_rules; + } + +RULE: foreach (@$rules) { + while (/\${user\.([a-z][a-zA-Z0-9]+)}/) { + my $k = lc $1; + my $v = $data{"user.$k"}; + next RULE unless defined $v; + next RULE if @$v != 1; + next RULE unless defined $v->[0]; + s/\${user\.$k}/$v->[0]/g; + } + if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { my $ops = $1; my $ref = $2; -- cgit v1.2.3 From d47eed3272311acf16f136c49b0bb341c9a6e39c Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 9 Aug 2007 02:38:12 -0400 Subject: Teach the update-paranoid to look at file differences In some applications of the update hook a user may be allowed to modify a branch, but only if the file level difference is also an allowed change. This is the commonly requested feature of allowing users to modify only certain files. A new repository.*.allow syntax permits granting the three basic file level operations: A: file is added relative to the other tree M: file exists in both trees, but its SHA-1 or mode differs D: file is removed relative to the other tree on a per-branch and path-name basis. The user must also have a branch level allow line already granting them access to create, rewind or update (CRU) that branch before the hook will consult any file level rules. In order for a branch change to succeed _all_ files that differ relative to some base (by default the old value of this branch, but it can also be any valid tree-ish) must be allowed by file level allow rules. A push is rejected if any diff exists that is not covered by at least one allow rule. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 112 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index fb2aca3628..84ed452480 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -102,6 +102,8 @@ my ($this_user) = getpwuid $<; # REAL_USER_ID my $repository_name; my %user_committer; my @allow_rules; +my @path_rules; +my %diff_cache; sub deny ($) { print STDERR "-Deny- $_[0]\n" if $debug; @@ -122,6 +124,13 @@ sub git_value (@) { open(T,'-|','git',@_); local $_ = ; chop; close T; $_; } +sub match_string ($$) { + my ($acl_n, $ref) = @_; + ($acl_n eq $ref) + || ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n) + || ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:); +} + sub parse_config ($$$$) { my $data = shift; local $ENV{GIT_DIR} = shift; @@ -209,6 +218,31 @@ sub check_committers (@) { } } +sub load_diff ($) { + my $base = shift; + my $d = $diff_cache{$base}; + unless ($d) { + local $/ = "\0"; + open(T,'-|','git','diff-tree', + '-r','--name-status','-z', + $base,$new) or return undef; + my %this_diff; + while () { + my $op = $_; + chop $op; + + my $path = ; + chop $path; + + $this_diff{$path} = $op; + } + close T or return undef; + $d = \%this_diff; + $diff_cache{$base} = $d; + } + return $d; +} + 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/,,; @@ -266,7 +300,19 @@ RULE: s/\${user\.$k}/$v->[0]/g; } - if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { + if (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)\s+diff\s+([^\s]+)$/) { + my ($ops, $pth, $ref, $bst) = ($1, $2, $3, $4); + $ops =~ s/ //g; + $pth =~ s/\\\\/\\/g; + $ref =~ s/\\\\/\\/g; + push @path_rules, [$ops, $pth, $ref, $bst]; + } elsif (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)$/) { + my ($ops, $pth, $ref) = ($1, $2, $3); + $ops =~ s/ //g; + $pth =~ s/\\\\/\\/g; + $ref =~ s/\\\\/\\/g; + push @path_rules, [$ops, $pth, $ref, $old]; + } elsif (/^([CDRU ]+)\s+for\s+([^\s]+)$/) { my $ops = $1; my $ref = $2; $ops =~ s/ //g; @@ -300,13 +346,65 @@ foreach my $acl_entry (@allow_rules) { next unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen. next unless $acl_n; next unless $op =~ /^[$acl_ops]$/; + next unless match_string $acl_n, $ref; + + # Don't test path rules on branch deletes. + # + grant "Allowed by: $acl_ops for $acl_n" if $op eq 'D'; + + # Aggregate matching path rules; allow if there aren't + # any matching this ref. + # + my %pr; + foreach my $p_entry (@path_rules) { + my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry; + next unless $p_ref; + push @{$pr{$p_bst}}, $p_entry if match_string $p_ref, $ref; + } + grant "Allowed by: $acl_ops for $acl_n" unless %pr; - 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:) - ); + # Allow only if all changes against a single base are + # allowed by file path rules. + # + my @bad; + foreach my $p_bst (keys %pr) { + my $diff_ref = load_diff $p_bst; + deny "Cannot difference trees." unless ref $diff_ref; + + my %fd = %$diff_ref; + foreach my $p_entry (@{$pr{$p_bst}}) { + my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry; + next unless $p_ops =~ /^[AMD]+$/; + next unless $p_n; + + foreach my $f_n (keys %fd) { + my $f_op = $fd{$f_n}; + next unless $f_op; + next unless $f_op =~ /^[$p_ops]$/; + delete $fd{$f_n} if match_string $p_n, $f_n; + } + last unless %fd; + } + + if (%fd) { + push @bad, [$p_bst, \%fd]; + } else { + # All changes relative to $p_bst were allowed. + # + grant "Allowed by: $acl_ops for $acl_n diff $p_bst"; + } + } + + foreach my $bad_ref (@bad) { + my ($p_bst, $fd) = @$bad_ref; + print STDERR "\n"; + print STDERR "Not allowed to make the following changes:\n"; + print STDERR "(base: $p_bst)\n"; + foreach my $f_n (sort keys %$fd) { + print STDERR " $fd->{$f_n} $f_n\n"; + } + } + deny "You are not permitted to $op $ref"; } close A; deny "You are not permitted to $op $ref"; -- cgit v1.2.3 From cabead982b7cf40f8930e6e38327fc396b7fef81 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 9 Aug 2007 02:38:16 -0400 Subject: Use the empty tree for base diff in paranoid-update on new branches We have to load a tree difference for the purpose of testing file patterns. But if our branch is being created and there is no specific base to difference against in the rule our base will be '0'x40. This is (usually) not a valid tree-ish object in a Git repository, so there's nothing to difference against. Instead of creating the empty tree and running git-diff against that we just take the output of `ls-tree -r --name-only` and mark every returned pathname as an add. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 84ed452480..068fa37083 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -223,20 +223,31 @@ sub load_diff ($) { my $d = $diff_cache{$base}; unless ($d) { local $/ = "\0"; - open(T,'-|','git','diff-tree', - '-r','--name-status','-z', - $base,$new) or return undef; my %this_diff; - while () { - my $op = $_; - chop $op; + if ($base =~ /^0{40}$/) { + open(T,'-|','git','ls-tree', + '-r','--name-only','-z', + $new) or return undef; + while () { + chop; + $this_diff{$_} = 'A'; + } + close T or return undef; + } else { + open(T,'-|','git','diff-tree', + '-r','--name-status','-z', + $base,$new) or return undef; + while () { + my $op = $_; + chop $op; - my $path = ; - chop $path; + my $path = ; + chop $path; - $this_diff{$path} = $op; + $this_diff{$path} = $op; + } + close T or return undef; } - close T or return undef; $d = \%this_diff; $diff_cache{$base} = $d; } -- cgit v1.2.3 From 09afcd6933e8497c997205dda71d718e62b4de62 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 11 Aug 2007 12:22:47 +0200 Subject: git.el: Add support for interactive diffs. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index f6102fc344..214b75cf93 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -965,7 +965,13 @@ Return the list of files that haven't been handled." (defun git-diff-file-idiff () "Perform an interactive diff on the current file." (interactive) - (error "Interactive diffs not implemented yet.")) + (let ((files (git-marked-files-state 'added 'deleted 'modified))) + (unless (eq 1 (length files)) + (error "Cannot perform an interactive diff on multiple files.")) + (let* ((filename (car (git-get-filenames files))) + (buff1 (find-file-noselect filename)) + (buff2 (git-run-command-buffer (concat filename ".~HEAD~") "cat-file" "blob" (concat "HEAD:" filename)))) + (ediff-buffers buff1 buff2)))) (defun git-log-file () "Display a log of changes to the marked file(s)." -- cgit v1.2.3 From 8fdc39729b9aaa02e89eca9d7964182a72052665 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 11 Aug 2007 12:23:21 +0200 Subject: git.el: Always set the current directory in the git-diff buffer. This allows jumping to the correct file with the diff-mode commands. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 214b75cf93..be44e06c45 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -912,10 +912,12 @@ Return the list of files that haven't been handled." (defun git-setup-diff-buffer (buffer) "Setup a buffer for displaying a diff." - (with-current-buffer buffer - (diff-mode) - (goto-char (point-min)) - (setq buffer-read-only t)) + (let ((dir default-directory)) + (with-current-buffer buffer + (diff-mode) + (goto-char (point-min)) + (setq default-directory dir) + (setq buffer-read-only t))) (display-buffer buffer) (shrink-window-if-larger-than-buffer)) -- cgit v1.2.3 From 7da660f4372f4113184f782634c0fafb2cf46cef Mon Sep 17 00:00:00 2001 From: "Reece H. Dunn" Date: Mon, 13 Aug 2007 19:40:50 +0100 Subject: git-p4: Fix the sorting of changelists when cloning a Perforce repository. When performing a git-p4 clone operation on a Perforce repository, where the changelists change in order of magnitude (e.g. 100 to 1000), the set of changes to import from is not sorted properly. This is because the data in the list is strings not integers. The other place where this is done already converts the value to an integer, so it is not affected. Acked-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 805d632a68..6d0106237a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1322,7 +1322,7 @@ class P4Sync(Command): for line in output: changeNum = line.split(" ")[1] - changes.append(changeNum) + changes.append(int(changeNum)) changes.sort() -- cgit v1.2.3 From 374a58c9fbd4613800d01087c77373e561db90ae Mon Sep 17 00:00:00 2001 From: Mark Levedahl Date: Sun, 12 Aug 2007 14:46:12 -0400 Subject: git-completion.bash - add support for git-bundle Signed-off-by: Mark Levedahl Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 82b9ed40d8..52b2893844 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -459,6 +459,35 @@ _git_branch () __gitcomp "$(__git_refs)" } +_git_bundle () +{ + local mycword="$COMP_CWORD" + case "${COMP_WORDS[0]}" in + git) + local cmd="${COMP_WORDS[2]}" + mycword="$((mycword-1))" + ;; + git-bundle*) + local cmd="${COMP_WORDS[1]}" + ;; + esac + case "$mycword" in + 1) + __gitcomp "create list-heads verify unbundle" + ;; + 2) + # looking for a file + ;; + *) + case "$cmd" in + create) + __git_complete_revlist + ;; + esac + ;; + esac +} + _git_checkout () { __gitcomp "$(__git_refs)" @@ -1009,6 +1038,7 @@ _git () add) _git_add ;; apply) _git_apply ;; bisect) _git_bisect ;; + bundle) _git_bundle ;; branch) _git_branch ;; checkout) _git_checkout ;; cherry) _git_cherry ;; @@ -1057,6 +1087,7 @@ complete -o default -o nospace -F _git_am git-am complete -o default -o nospace -F _git_apply git-apply complete -o default -o nospace -F _git_bisect git-bisect complete -o default -o nospace -F _git_branch git-branch +complete -o default -o nospace -F _git_bundle git-bundle complete -o default -o nospace -F _git_checkout git-checkout complete -o default -o nospace -F _git_cherry git-cherry complete -o default -o nospace -F _git_cherry_pick git-cherry-pick @@ -1092,6 +1123,7 @@ complete -o default -o nospace -F _git_add git-add.exe complete -o default -o nospace -F _git_apply git-apply.exe complete -o default -o nospace -F _git git.exe complete -o default -o nospace -F _git_branch git-branch.exe +complete -o default -o nospace -F _git_bundle git-bundle.exe complete -o default -o nospace -F _git_cherry git-cherry.exe complete -o default -o nospace -F _git_diff git-diff.exe complete -o default -o nospace -F _git_format_patch git-format-patch.exe -- cgit v1.2.3 From e301bfeea19e284344868840793c58d2e7529c74 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 21 Aug 2007 21:50:12 -0400 Subject: Fix new-workdir (again) to work on bare repositories My day-job workflow involves using multiple workdirs attached to a bunch of bare repositories. Such repositories are stored inside of a directory called "foo.git", which means `git rev-parse --git-dir` will return "." and not ".git". Under such conditions new-workdir was getting confused about where the Git repository it was supplied is actually located. If we get "." for the result of --git-dir query it means we should use the user supplied path as-is, and not attempt to perform any magic on it, as the path is directly to the repository. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 3ff6bd166a..119cff9859 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -24,10 +24,14 @@ git_dir=$(cd "$orig_git" 2>/dev/null && git rev-parse --git-dir 2>/dev/null) || die "\"$orig_git\" is not a git repository!" -if test "$git_dir" = ".git" -then +case "$git_dir" in +.git) git_dir="$orig_git/.git" -fi + ;; +.) + git_dir=$orig_git + ;; +esac # don't link to a workdir if test -L "$git_dir/config" -- cgit v1.2.3 From 8fa0ee3b50736eb869a3e13375bb041c1bf5aa12 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 22 Aug 2007 01:33:49 -0400 Subject: Suggest unsetting core.bare when using new-workdir on a bare repository If core.bare is set to true in the config file of a repository that the user is trying to create a working directory from we should abort and suggest to the user that they remove the option first. If we leave the core.bare=true setting in the config file then working tree operations will get confused when they attempt to execute in the new workdir, as it shares its config file with the bare repository. The working tree operations will assume that the workdir is bare and abort, which is not what the user wants. If we changed core.bare to be false then working tree operations will function in the workdir but other operations may fail in the bare repository, as it claims to not be bare. If we remove core.bare from the config then Git can fallback on the legacy guessing behavior. This allows operations in the bare repository to work as though it were bare, while operations in the workdirs to act as though they are not bare. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 119cff9859..c6e154a84f 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -33,6 +33,14 @@ case "$git_dir" in ;; esac +# don't link to a configured bare repository +isbare=$(git --git-dir="$git_dir" config --bool --get core.bare) +if test ztrue = z$isbare +then + die "\"$git_dir\" has core.bare set to true," \ + " remove from \"$git_dir/config\" to use $0" +fi + # don't link to a workdir if test -L "$git_dir/config" then -- cgit v1.2.3 From ef08c14993a8d2881941667c0264c97c6874ccee Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 22 Aug 2007 12:21:38 +0200 Subject: git.el: Avoid a lisp error when there's no current branch (detached HEAD). Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index be44e06c45..abc799a287 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -664,9 +664,11 @@ Return the list of files that haven't been handled." (ewoc-set-hf status (format "Directory: %s\nBranch: %s\nHead: %s%s\n" default-directory - (if (string-match "^refs/heads/" branch) - (substring branch (match-end 0)) - branch) + (if branch + (if (string-match "^refs/heads/" branch) + (substring branch (match-end 0)) + branch) + "none (detached HEAD)") head (if merge-heads (concat "\nMerging: " -- cgit v1.2.3 From 47e98eecf3721dca64e3d453fc96e3a02debe54f Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 23 Aug 2007 01:39:22 -0400 Subject: Update bash completion with new 1.5.3 command line options A number of commands have learned new tricks as part of git 1.5.3. If these are long options (--foo) we tend to support them in the bash completion, as it makes the user's task of using the option slightly easier. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 52b2893844..8f27aa9a13 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -419,7 +419,7 @@ _git_add () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--interactive" + __gitcomp "--interactive --refresh" return esac COMPREPLY=() @@ -573,6 +573,7 @@ _git_format_patch () --stdout --attach --thread --output-directory --numbered --start-number + --numbered-files --keep-subject --signoff --in-reply-to= @@ -590,7 +591,7 @@ _git_gc () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--prune" + __gitcomp "--prune --aggressive" return ;; esac @@ -617,14 +618,20 @@ _git_log () " "" "${cur##--pretty=}" return ;; + --date=*) + __gitcomp " + relative iso8601 rfc2822 short local default + " "" "${cur##--date=}" + return + ;; --*) __gitcomp " --max-count= --max-age= --since= --after= --min-age= --before= --until= --root --topo-order --date-order --reverse - --no-merges + --no-merges --follow --abbrev-commit --abbrev= - --relative-date + --relative-date --date= --author= --committer= --grep= --all-match --pretty= --name-status --name-only --raw @@ -796,7 +803,7 @@ _git_config () case "$cur" in --*) __gitcomp " - --global --system + --global --system --file= --list --replace-all --get --get-all --get-regexp --add --unset --unset-all @@ -839,6 +846,7 @@ _git_config () core.ignoreStat core.preferSymlinkRefs core.logAllRefUpdates + core.loosecompression core.repositoryFormatVersion core.sharedRepository core.warnAmbiguousRefs @@ -870,6 +878,7 @@ _git_config () diff.renames fetch.unpackLimit format.headers + format.subjectprefix gitcvs.enabled gitcvs.logfile gitcvs.allbinary @@ -896,6 +905,10 @@ _git_config () merge.verbosity pack.window pack.depth + pack.windowMemory + pack.compression + pack.deltaCacheSize + pack.deltaCacheLimit pull.octopus pull.twohead repack.useDeltaBaseOffset @@ -1024,7 +1037,14 @@ _git () if [ $c -eq $COMP_CWORD -a -z "$command" ]; then case "${COMP_WORDS[COMP_CWORD]}" in --*=*) COMPREPLY=() ;; - --*) __gitcomp "--git-dir= --bare --version --exec-path" ;; + --*) __gitcomp " + --no-pager + --git-dir= + --bare + --version + --exec-path + " + ;; *) __gitcomp "$(__git_commands) $(__git_aliases)" ;; esac return -- cgit v1.2.3 From 217926c08cb634e3b5394ea15b3fe4520069260b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 23 Aug 2007 01:42:11 -0400 Subject: Teach bash to complete ref arguments to git-describe I'm often finding that I need to run git-describe on very long remote tracking branch names, to find out what tagged revision the remote tracking branch is now at (or not at). Typing out the ref names is painful, so bash completion on them is a very useful feature. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8f27aa9a13..6ed6a51dc6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -525,6 +525,11 @@ _git_commit () COMPREPLY=() } +_git_describe () +{ + __gitcomp "$(__git_refs)" +} + _git_diff () { __git_complete_file @@ -1065,6 +1070,7 @@ _git () cherry-pick) _git_cherry_pick ;; commit) _git_commit ;; config) _git_config ;; + describe) _git_describe ;; diff) _git_diff ;; fetch) _git_fetch ;; format-patch) _git_format_patch ;; @@ -1112,6 +1118,7 @@ complete -o default -o nospace -F _git_checkout git-checkout complete -o default -o nospace -F _git_cherry git-cherry complete -o default -o nospace -F _git_cherry_pick git-cherry-pick complete -o default -o nospace -F _git_commit git-commit +complete -o default -o nospace -F _git_describe git-describe complete -o default -o nospace -F _git_diff git-diff complete -o default -o nospace -F _git_fetch git-fetch complete -o default -o nospace -F _git_format_patch git-format-patch @@ -1145,6 +1152,7 @@ complete -o default -o nospace -F _git git.exe complete -o default -o nospace -F _git_branch git-branch.exe complete -o default -o nospace -F _git_bundle git-bundle.exe complete -o default -o nospace -F _git_cherry git-cherry.exe +complete -o default -o nospace -F _git_describe git-describe.exe complete -o default -o nospace -F _git_diff git-diff.exe complete -o default -o nospace -F _git_format_patch git-format-patch.exe complete -o default -o nospace -F _git_log git-log.exe -- cgit v1.2.3 From be86f7a0dfa05dacb2ef512e99d1de576e77a633 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Thu, 23 Aug 2007 01:50:49 -0400 Subject: Teach bash about git-submodule and its subcommands The git-submodule command is new in 1.5.3 and contains a number of useful subcommands for working on submodules. We usually try to offer the subcommands of a git command in the bash completion, so here they are for git-submodule. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6ed6a51dc6..a652c88b27 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1024,6 +1024,31 @@ _git_stash () __gitcomp 'list show apply clear' } +_git_submodule () +{ + local i c=1 command + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + add|status|init|update) command="$i"; break ;; + esac + c=$((++c)) + done + + if [ $c -eq $COMP_CWORD -a -z "$command" ]; then + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--quiet --cached" + ;; + *) + __gitcomp "add status init update" + ;; + esac + return + fi +} + _git () { local i c=1 command __git_dir @@ -1090,6 +1115,7 @@ _git () show) _git_show ;; show-branch) _git_log ;; stash) _git_stash ;; + submodule) _git_submodule ;; whatchanged) _git_log ;; *) COMPREPLY=() ;; esac @@ -1138,6 +1164,7 @@ complete -o default -o nospace -F _git_reset git-reset complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_stash git-stash +complete -o default -o nospace -F _git_submodule git-submodule complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_log git-whatchanged -- cgit v1.2.3 From 5ca4461728adadf22b5b8833693702ab09e556db Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 24 Aug 2007 17:44:16 +0200 Subject: git-p4: Make 'git-p4 branches' work after an initial clone with git clone from an origin-updated repository. After a clone with "git clone" of a repository the p4 branches are only in remotes/origin/p4/* and not in remotes/p4/*. Separate the code for detection and creation out of the P4Sync command class into standalone methods and use them from the P4Branches command. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 104 ++++++++++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 49 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6d0106237a..b571e30473 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -231,6 +231,56 @@ def findUpstreamBranchPoint(head = "HEAD"): return ["", settings] +def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True): + if not silent: + print ("Creating/updating branch(es) in %s based on origin branch(es)" + % localRefPrefix) + + originPrefix = "origin/p4/" + + for line in read_pipe_lines("git rev-parse --symbolic --remotes"): + line = line.strip() + if (not line.startswith(originPrefix)) or line.endswith("HEAD"): + continue + + headName = line[len(originPrefix):] + remoteHead = localRefPrefix + headName + originHead = line + + original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead)) + if (not original.has_key('depot-paths') + or not original.has_key('change')): + continue + + update = False + if not gitBranchExists(remoteHead): + if verbose: + print "creating %s" % remoteHead + update = True + else: + settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead)) + if settings.has_key('change') > 0: + if settings['depot-paths'] == original['depot-paths']: + originP4Change = int(original['change']) + p4Change = int(settings['change']) + if originP4Change > p4Change: + print ("%s (%s) is newer than %s (%s). " + "Updating p4 branch from origin." + % (originHead, originP4Change, + remoteHead, p4Change)) + update = True + else: + print ("Ignoring: %s was imported from %s while " + "%s was imported from %s" + % (originHead, ','.join(original['depot-paths']), + remoteHead, ','.join(settings['depot-paths']))) + + if update: + system("git update-ref %s %s" % (remoteHead, originHead)) + +def originP4BranchesExist(): + return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") + class Command: def __init__(self): self.usage = "usage: %prog [options]" @@ -1041,53 +1091,6 @@ class P4Sync(Command): for branch in branches.keys(): self.initialParents[self.refPrefix + branch] = branches[branch] - def createOrUpdateBranchesFromOrigin(self): - if not self.silent: - print ("Creating/updating branch(es) in %s based on origin branch(es)" - % self.refPrefix) - - originPrefix = "origin/p4/" - - for line in read_pipe_lines("git rev-parse --symbolic --remotes"): - line = line.strip() - if (not line.startswith(originPrefix)) or line.endswith("HEAD"): - continue - - headName = line[len(originPrefix):] - remoteHead = self.refPrefix + headName - originHead = line - - original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead)) - if (not original.has_key('depot-paths') - or not original.has_key('change')): - continue - - update = False - if not gitBranchExists(remoteHead): - if self.verbose: - print "creating %s" % remoteHead - update = True - else: - settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead)) - if settings.has_key('change') > 0: - if settings['depot-paths'] == original['depot-paths']: - originP4Change = int(original['change']) - p4Change = int(settings['change']) - if originP4Change > p4Change: - print ("%s (%s) is newer than %s (%s). " - "Updating p4 branch from origin." - % (originHead, originP4Change, - remoteHead, p4Change)) - update = True - else: - print ("Ignoring: %s was imported from %s while " - "%s was imported from %s" - % (originHead, ','.join(original['depot-paths']), - remoteHead, ','.join(settings['depot-paths']))) - - if update: - system("git update-ref %s %s" % (remoteHead, originHead)) - def updateOptionDict(self, d): option_keys = {} if self.keepRepoPath: @@ -1108,7 +1111,7 @@ class P4Sync(Command): # map from branch depot path to parent branch self.knownBranches = {} self.initialParents = {} - self.hasOrigin = gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") + self.hasOrigin = originP4BranchesExist() if not self.syncWithOrigin: self.hasOrigin = False @@ -1135,7 +1138,7 @@ class P4Sync(Command): # merge with previous imports, if possible. if args == []: if self.hasOrigin: - self.createOrUpdateBranchesFromOrigin() + createOrUpdateBranchesFromOrigin(self.refPrefix, self.silent) self.listExistingP4GitBranches() if len(self.p4BranchesInGit) > 1: @@ -1518,6 +1521,9 @@ class P4Branches(Command): self.verbose = False def run(self, args): + if originP4BranchesExist(): + createOrUpdateBranchesFromOrigin() + cmdline = "git rev-parse --symbolic " cmdline += " --remotes" -- cgit v1.2.3 From 0058a33a8eb5a8bfcfc5f5e769a2517cff4b73f1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 24 Aug 2007 17:46:16 +0200 Subject: git-p4: Fix warnings about non-existant refs/remotes/p4/HEAD ref when running git-p4 sync the first time after a git clone. Don't create the p4/HEAD symbolic ref if p4/master doesn't exist yet. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b571e30473..55778c5775 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1131,7 +1131,7 @@ class P4Sync(Command): system("git update-ref %s refs/heads/p4" % self.branch) system("git branch -D p4"); # create it /after/ importing, when master exists - if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes: + if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch): system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) # TODO: should always look at previous commits, -- cgit v1.2.3 From 1ff55ff27b819c1fd9c0d2e153974f2c1924a875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Mon, 27 Aug 2007 11:50:12 +0200 Subject: git.el: Added colors for dark background Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index abc799a287..280557ecd4 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -99,47 +99,56 @@ if there is already one that displays the same directory." (defface git-status-face - '((((class color) (background light)) (:foreground "purple"))) + '((((class color) (background light)) (:foreground "purple")) + (((class color) (background dark)) (:foreground "salmon"))) "Git mode face used to highlight added and modified files." :group 'git) (defface git-unmerged-face - '((((class color) (background light)) (:foreground "red" :bold t))) + '((((class color) (background light)) (:foreground "red" :bold t)) + (((class color) (background dark)) (:foreground "red" :bold t))) "Git mode face used to highlight unmerged files." :group 'git) (defface git-unknown-face - '((((class color) (background light)) (:foreground "goldenrod" :bold t))) + '((((class color) (background light)) (:foreground "goldenrod" :bold t)) + (((class color) (background dark)) (:foreground "goldenrod" :bold t))) "Git mode face used to highlight unknown files." :group 'git) (defface git-uptodate-face - '((((class color) (background light)) (:foreground "grey60"))) + '((((class color) (background light)) (:foreground "grey60")) + (((class color) (background dark)) (:foreground "grey40"))) "Git mode face used to highlight up-to-date files." :group 'git) (defface git-ignored-face - '((((class color) (background light)) (:foreground "grey60"))) + '((((class color) (background light)) (:foreground "grey60")) + (((class color) (background dark)) (:foreground "grey40"))) "Git mode face used to highlight ignored files." :group 'git) (defface git-mark-face - '((((class color) (background light)) (:foreground "red" :bold t))) + '((((class color) (background light)) (:foreground "red" :bold t)) + (((class color) (background dark)) (:foreground "tomato" :bold t))) "Git mode face used for the file marks." :group 'git) (defface git-header-face - '((((class color) (background light)) (:foreground "blue"))) + '((((class color) (background light)) (:foreground "blue")) + (((class color) (background dark)) (:foreground "blue"))) "Git mode face used for commit headers." :group 'git) (defface git-separator-face - '((((class color) (background light)) (:foreground "brown"))) + '((((class color) (background light)) (:foreground "brown")) + (((class color) (background dark)) (:foreground "brown"))) "Git mode face used for commit separator." :group 'git) (defface git-permission-face - '((((class color) (background light)) (:foreground "green" :bold t))) + '((((class color) (background light)) (:foreground "green" :bold t)) + (((class color) (background dark)) (:foreground "green" :bold t))) "Git mode face used for permission changes." :group 'git) -- cgit v1.2.3 From 7d37b5bf4eda8b2dcca371fb4b5ebd50fed64d67 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 29 Aug 2007 15:15:34 +0100 Subject: completion: also complete git-log's --left-right and --cherry-pick option Both --left-right and --cherry-pick are particularly long to type, so help the user there. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a652c88b27..5ed18215fd 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -641,6 +641,7 @@ _git_log () --all-match --pretty= --name-status --name-only --raw --not --all + --left-right --cherry-pick " return ;; -- cgit v1.2.3 From 88e21dc7461dca1ebc70d8579bcc9246364511ee Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Fri, 31 Aug 2007 23:47:01 -0400 Subject: Teach bash about completing arguments for git-tag Lately I have been doing a lot of calls to `git tag -d` and also to `git tag -v`. In both such cases being able to complete the names of existing tags saves the fingers some typing effort. We now look for the -d or -v option to git-tag in the bash completion support and offer up existing tag names as possible choices for these. When creating a new tag we now also offer bash completion support for the second argument to git-tag (the object to be tagged) as this can often be a specific existing branch name and is not necessarily the current HEAD. If the -f option is being used to recreate an existing tag we now also offer completion support on the existing tag names for the first argument of git-tag, helping to the user to reselect the prior tag name that they are trying to replace. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 5ed18215fd..cad842af45 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -114,6 +114,27 @@ __git_heads () done } +__git_tags () +{ + local cmd i is_hash=y dir="$(__gitdir "$1")" + if [ -d "$dir" ]; then + for i in $(git --git-dir="$dir" \ + for-each-ref --format='%(refname)' \ + refs/tags ); do + echo "${i#refs/tags/}" + done + return + fi + for i in $(git-ls-remote "$1" 2>/dev/null); do + case "$is_hash,$i" in + y,*) is_hash=n ;; + n,*^{}) is_hash=y ;; + n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;; + n,*) is_hash=y; echo "$i" ;; + esac + done +} + __git_refs () { local cmd i is_hash=y dir="$(__gitdir "$1")" @@ -1050,6 +1071,40 @@ _git_submodule () fi } +_git_tag () +{ + local i c=1 f=0 + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + -d|-v) + __gitcomp "$(__git_tags)" + return + ;; + -f) + f=1 + ;; + esac + c=$((++c)) + done + + case "${COMP_WORDS[COMP_CWORD-1]}" in + -m|-F) + COMPREPLY=() + ;; + -*|tag|git-tag) + if [ $f = 1 ]; then + __gitcomp "$(__git_tags)" + else + COMPREPLY=() + fi + ;; + *) + __gitcomp "$(__git_refs)" + ;; + esac +} + _git () { local i c=1 command __git_dir @@ -1117,6 +1172,7 @@ _git () show-branch) _git_log ;; stash) _git_stash ;; submodule) _git_submodule ;; + tag) _git_tag ;; whatchanged) _git_log ;; *) COMPREPLY=() ;; esac @@ -1167,6 +1223,7 @@ complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_stash git-stash complete -o default -o nospace -F _git_submodule git-submodule complete -o default -o nospace -F _git_log git-show-branch +complete -o default -o nospace -F _git_tag git-tag complete -o default -o nospace -F _git_log git-whatchanged # The following are necessary only for Cygwin, and only are needed @@ -1192,5 +1249,6 @@ complete -o default -o nospace -F _git_config git-config complete -o default -o nospace -F _git_shortlog git-shortlog.exe complete -o default -o nospace -F _git_show git-show.exe complete -o default -o nospace -F _git_log git-show-branch.exe +complete -o default -o nospace -F _git_tag git-tag.exe complete -o default -o nospace -F _git_log git-whatchanged.exe fi -- cgit v1.2.3 From 31f9ec129ef37e50b5cacf26a2ebcb5420fcdc5e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 21 Aug 2007 11:53:02 +0200 Subject: git-p4: Always call 'p4 sync ...' before submitting to Perforce. Acked-by: Marius Storm-Olsen Acked-by: Thiago Macieira --- contrib/fast-import/git-p4 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 55778c5775..3728cbf9aa 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -664,9 +664,8 @@ class P4Submit(Command): f.close(); os.chdir(self.clientPath) - response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % self.clientPath) - if response == "y" or response == "yes": - system("p4 sync ...") + print "Syncronizing p4 checkout..." + system("p4 sync ...") if self.reset: self.firstTime = True -- cgit v1.2.3 From 14594f4b5747e51b051f647f6430089e6664e77d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 22 Aug 2007 09:07:15 +0200 Subject: git-p4: After submission to p4 always synchronize from p4 again (into refs/remotes). Whether to rebase HEAD or not is still left as question to the end-user. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3728cbf9aa..16e0a7bc81 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -704,10 +704,14 @@ class P4Submit(Command): else: print "All changes applied!" os.chdir(self.oldWorkingDirectory) - response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ") + + sync = P4Sync() + sync.run([]) + + response = raw_input("Do you want to rebase current HEAD from Perforce now using git-p4 rebase? [y]es/[n]o ") if response == "y" or response == "yes": rebase = P4Rebase() - rebase.run([]) + rebase.rebase() os.remove(self.configFile) return True @@ -1439,6 +1443,9 @@ class P4Rebase(Command): sync = P4Sync() sync.run([]) + return self.rebase() + + def rebase(self): [upstream, settings] = findUpstreamBranchPoint() if len(upstream) == 0: die("Cannot find upstream branchpoint for rebase") -- cgit v1.2.3 From 4f6432d8cc6ebdcdc366cf67ab39e8125c449d80 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 15:56:36 +0200 Subject: git-p4: Cleanup; moved the code for getting a sorted list of p4 changes for a list of given depot paths into a standalone method. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 16e0a7bc81..e9feb7498c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -281,6 +281,19 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent def originP4BranchesExist(): return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master") +def p4ChangesForPaths(depotPaths, changeRange): + assert depotPaths + output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, changeRange) + for p in depotPaths])) + + changes = [] + for line in output: + changeNum = line.split(" ")[1] + changes.append(int(changeNum)) + + changes.sort() + return changes + class Command: def __init__(self): self.usage = "usage: %prog [options]" @@ -1322,15 +1335,7 @@ class P4Sync(Command): if self.verbose: print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths), self.changeRange) - assert self.depotPaths - output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange) - for p in self.depotPaths])) - - for line in output: - changeNum = line.split(" ")[1] - changes.append(int(changeNum)) - - changes.sort() + changes = p4ChangesForPaths(self.depotPaths, self.changeRange) if len(self.maxChanges) > 0: changes = changes[:min(int(self.maxChanges), len(changes))] -- cgit v1.2.3 From e87f37ae42dad89b39620c234fc29c94529a4d07 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 16:00:52 +0200 Subject: git-p4: Cleanup; moved the code to import a list of p4 changes using fast-import into a separate member function of P4Sync. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 140 +++++++++++++++++++++++---------------------- 1 file changed, 71 insertions(+), 69 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e9feb7498c..c00702c895 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1118,6 +1118,76 @@ class P4Sync(Command): self.keepRepoPath = (d.has_key('options') and ('keepRepoPath' in d['options'])) + def importChanges(self, changes): + cnt = 1 + for change in changes: + description = p4Cmd("describe %s" % change) + self.updateOptionDict(description) + + if not self.silent: + sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) + sys.stdout.flush() + cnt = cnt + 1 + + try: + if self.detectBranches: + branches = self.splitFilesIntoBranches(description) + for branch in branches.keys(): + ## HACK --hwn + branchPrefix = self.depotPaths[0] + branch + "/" + + parent = "" + + filesForCommit = branches[branch] + + if self.verbose: + print "branch is %s" % branch + + self.updatedBranches.add(branch) + + if branch not in self.createdBranches: + self.createdBranches.add(branch) + parent = self.knownBranches[branch] + if parent == branch: + parent = "" + elif self.verbose: + print "parent determined through known branches: %s" % parent + + # main branch? use master + if branch == "main": + branch = "master" + else: + + ## FIXME + branch = self.projectName + branch + + if parent == "main": + parent = "master" + elif len(parent) > 0: + ## FIXME + parent = self.projectName + parent + + branch = self.refPrefix + branch + if len(parent) > 0: + parent = self.refPrefix + parent + + if self.verbose: + print "looking for initial parent for %s; current parent is %s" % (branch, parent) + + if len(parent) == 0 and branch in self.initialParents: + parent = self.initialParents[branch] + del self.initialParents[branch] + + self.commit(description, filesForCommit, branch, [branchPrefix], parent) + else: + files = self.extractFilesFromCommit(description) + self.commit(description, files, self.branch, self.depotPaths, + self.initialParent) + self.initialParent = "" + except IOError: + print self.gitError.read() + sys.exit(1) + def run(self, args): self.depotPaths = [] self.changeRange = "" @@ -1350,74 +1420,7 @@ class P4Sync(Command): self.updatedBranches = set() - cnt = 1 - for change in changes: - description = p4Cmd("describe %s" % change) - self.updateOptionDict(description) - - if not self.silent: - sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes))) - sys.stdout.flush() - cnt = cnt + 1 - - try: - if self.detectBranches: - branches = self.splitFilesIntoBranches(description) - for branch in branches.keys(): - ## HACK --hwn - branchPrefix = self.depotPaths[0] + branch + "/" - - parent = "" - - filesForCommit = branches[branch] - - if self.verbose: - print "branch is %s" % branch - - self.updatedBranches.add(branch) - - if branch not in self.createdBranches: - self.createdBranches.add(branch) - parent = self.knownBranches[branch] - if parent == branch: - parent = "" - elif self.verbose: - print "parent determined through known branches: %s" % parent - - # main branch? use master - if branch == "main": - branch = "master" - else: - - ## FIXME - branch = self.projectName + branch - - if parent == "main": - parent = "master" - elif len(parent) > 0: - ## FIXME - parent = self.projectName + parent - - branch = self.refPrefix + branch - if len(parent) > 0: - parent = self.refPrefix + parent - - if self.verbose: - print "looking for initial parent for %s; current parent is %s" % (branch, parent) - - if len(parent) == 0 and branch in self.initialParents: - parent = self.initialParents[branch] - del self.initialParents[branch] - - self.commit(description, filesForCommit, branch, [branchPrefix], parent) - else: - files = self.extractFilesFromCommit(description) - self.commit(description, files, self.branch, self.depotPaths, - self.initialParent) - self.initialParent = "" - except IOError: - print self.gitError.read() - sys.exit(1) + self.importChanges(changes) if not self.silent: print "" @@ -1427,7 +1430,6 @@ class P4Sync(Command): sys.stdout.write("%s " % b) sys.stdout.write("\n") - self.gitStream.close() if importProcess.wait() != 0: die("fast-import failed: %s" % self.gitError.read()) -- cgit v1.2.3 From 1c49fc197bd05a4c2ed602efcdbe277ef798813a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 16:04:34 +0200 Subject: git-p4: Cleanup; Turn self.revision into a function local variable (it's not used anywhere outside the function). Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c00702c895..d7c5becc0e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1285,7 +1285,7 @@ class P4Sync(Command): self.depotPaths = sorted(args) - self.revision = "" + revision = "" self.users = {} newPaths = [] @@ -1296,15 +1296,15 @@ class P4Sync(Command): if self.changeRange == "@all": self.changeRange = "" elif ',' not in self.changeRange: - self.revision = self.changeRange + revision = self.changeRange self.changeRange = "" p = p[:atIdx] elif p.find("#") != -1: hashIdx = p.index("#") - self.revision = p[hashIdx:] + revision = p[hashIdx:] p = p[:hashIdx] elif self.previousDepotPaths == []: - self.revision = "#head" + revision = "#head" p = re.sub ("\.\.\.$", "", p) if not p.endswith("/"): @@ -1345,19 +1345,19 @@ class P4Sync(Command): self.gitStream = importProcess.stdin self.gitError = importProcess.stderr - if self.revision: - print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), self.revision, self.branch) + if revision: + print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch) details = { "user" : "git perforce import user", "time" : int(time.time()) } details["desc"] = ("Initial import of %s from the state at revision %s" - % (' '.join(self.depotPaths), self.revision)) - details["change"] = self.revision + % (' '.join(self.depotPaths), revision)) + details["change"] = revision newestRevision = 0 fileCnt = 0 for info in p4CmdList("files " + ' '.join(["%s...%s" - % (p, self.revision) + % (p, revision) for p in self.depotPaths])): if info['code'] == 'error': -- cgit v1.2.3 From c208a24310582d9cf337b66f41a0d7a9fe106bb4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 16:07:18 +0200 Subject: git-p4: Cleanup; moved the code for the initial #head or revision import into a separate function, out of P4Sync.run. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 87 ++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 42 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d7c5becc0e..2c67190ffc 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1188,6 +1188,50 @@ class P4Sync(Command): print self.gitError.read() sys.exit(1) + def importHeadRevision(self, revision): + print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch) + + details = { "user" : "git perforce import user", "time" : int(time.time()) } + details["desc"] = ("Initial import of %s from the state at revision %s" + % (' '.join(self.depotPaths), revision)) + details["change"] = revision + newestRevision = 0 + + fileCnt = 0 + for info in p4CmdList("files " + + ' '.join(["%s...%s" + % (p, revision) + for p in self.depotPaths])): + + if info['code'] == 'error': + sys.stderr.write("p4 returned an error: %s\n" + % info['data']) + sys.exit(1) + + + change = int(info["change"]) + if change > newestRevision: + newestRevision = change + + if info["action"] == "delete": + # don't increase the file cnt, otherwise details["depotFile123"] will have gaps! + #fileCnt = fileCnt + 1 + continue + + for prop in ["depotFile", "rev", "action", "type" ]: + details["%s%s" % (prop, fileCnt)] = info[prop] + + fileCnt = fileCnt + 1 + + details["change"] = newestRevision + self.updateOptionDict(details) + try: + self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths) + except IOError: + print "IO error with git fast-import. Is your git version recent enough?" + print self.gitError.read() + + def run(self, args): self.depotPaths = [] self.changeRange = "" @@ -1346,48 +1390,7 @@ class P4Sync(Command): self.gitError = importProcess.stderr if revision: - print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch) - - details = { "user" : "git perforce import user", "time" : int(time.time()) } - details["desc"] = ("Initial import of %s from the state at revision %s" - % (' '.join(self.depotPaths), revision)) - details["change"] = revision - newestRevision = 0 - - fileCnt = 0 - for info in p4CmdList("files " - + ' '.join(["%s...%s" - % (p, revision) - for p in self.depotPaths])): - - if info['code'] == 'error': - sys.stderr.write("p4 returned an error: %s\n" - % info['data']) - sys.exit(1) - - - change = int(info["change"]) - if change > newestRevision: - newestRevision = change - - if info["action"] == "delete": - # don't increase the file cnt, otherwise details["depotFile123"] will have gaps! - #fileCnt = fileCnt + 1 - continue - - for prop in ["depotFile", "rev", "action", "type" ]: - details["%s%s" % (prop, fileCnt)] = info[prop] - - fileCnt = fileCnt + 1 - - details["change"] = newestRevision - self.updateOptionDict(details) - try: - self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths) - except IOError: - print "IO error with git fast-import. Is your git version recent enough?" - print self.gitError.read() - + self.importHeadRevision(revision) else: changes = [] -- cgit v1.2.3 From 8134f69c21ff47283d8b3ea3cc5b306727cde256 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 16:44:55 +0200 Subject: git-p4: Cleanup; moved the (duplicated) code for turning a branch into a git ref (for example foo -> refs/remotes/p4//foo) into a separate method. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2c67190ffc..406bec1a29 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1118,6 +1118,15 @@ class P4Sync(Command): self.keepRepoPath = (d.has_key('options') and ('keepRepoPath' in d['options'])) + def gitRefForBranch(self, branch): + if branch == "main": + return self.refPrefix + "master" + + if len(branch) <= 0: + return branch + + return self.refPrefix + self.projectName + branch + def importChanges(self, changes): cnt = 1 for change in changes: @@ -1153,23 +1162,8 @@ class P4Sync(Command): elif self.verbose: print "parent determined through known branches: %s" % parent - # main branch? use master - if branch == "main": - branch = "master" - else: - - ## FIXME - branch = self.projectName + branch - - if parent == "main": - parent = "master" - elif len(parent) > 0: - ## FIXME - parent = self.projectName + parent - - branch = self.refPrefix + branch - if len(parent) > 0: - parent = self.refPrefix + parent + branch = self.gitRefForBranch(branch) + parent = self.gitRefForBranch(parent) if self.verbose: print "looking for initial parent for %s; current parent is %s" % (branch, parent) -- cgit v1.2.3 From 1ca3d71069620f1438d9f89165a3e69e8d47d302 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 26 Aug 2007 17:36:55 +0200 Subject: git-p4: Added support for automatically importing newly appearing perforce branches. If a change in a p4 "branch" appears that hasn't seen any previous commit and that has a known branch mapping we now try to import it properly. First we find the p4 change of the source branch that the new p4 branch is based on. Then we using git rev-list --bisect to locate the corresponding git commit to that change. Finally we import all changes in the new p4 branch up to the current change and resume with the regular import. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 76 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 406bec1a29..adaaae6633 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1127,6 +1127,67 @@ class P4Sync(Command): return self.refPrefix + self.projectName + branch + def gitCommitByP4Change(self, ref, change): + if self.verbose: + print "looking in ref " + ref + " for change %s using bisect..." % change + + earliestCommit = "" + latestCommit = parseRevision(ref) + + while True: + if self.verbose: + print "trying: earliest %s latest %s" % (earliestCommit, latestCommit) + next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip() + if len(next) == 0: + if self.verbose: + print "argh" + return "" + log = extractLogMessageFromGitCommit(next) + settings = extractSettingsGitLog(log) + currentChange = int(settings['change']) + if self.verbose: + print "current change %s" % currentChange + + if currentChange == change: + if self.verbose: + print "found %s" % next + return next + + if currentChange < change: + earliestCommit = "^%s" % next + else: + latestCommit = "%s" % next + + return "" + + def importNewBranch(self, branch, maxChange): + # make fast-import flush all changes to disk and update the refs using the checkpoint + # command so that we can try to find the branch parent in the git history + self.gitStream.write("checkpoint\n\n"); + self.gitStream.flush(); + branchPrefix = self.depotPaths[0] + branch + "/" + range = "@1,%s" % maxChange + #print "prefix" + branchPrefix + changes = p4ChangesForPaths([branchPrefix], range) + if len(changes) <= 0: + return False + firstChange = changes[0] + #print "first change in branch: %s" % firstChange + sourceBranch = self.knownBranches[branch] + sourceDepotPath = self.depotPaths[0] + sourceBranch + sourceRef = self.gitRefForBranch(sourceBranch) + #print "source " + sourceBranch + + branchParentChange = int(p4Cmd("changes -m 1 %s...@1,%s" % (sourceDepotPath, firstChange))["change"]) + #print "branch parent: %s" % branchParentChange + gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange) + if len(gitParent) > 0: + self.initialParents[self.gitRefForBranch(branch)] = gitParent + #print "parent git commit: %s" % gitParent + + self.importChanges(changes) + return True + def importChanges(self, changes): cnt = 1 for change in changes: @@ -1159,8 +1220,19 @@ class P4Sync(Command): parent = self.knownBranches[branch] if parent == branch: parent = "" - elif self.verbose: - print "parent determined through known branches: %s" % parent + else: + fullBranch = self.projectName + branch + if fullBranch not in self.p4BranchesInGit: + if not self.silent: + print("\n Importing new branch %s" % fullBranch); + if self.importNewBranch(branch, change - 1): + parent = "" + self.p4BranchesInGit.append(fullBranch) + if not self.silent: + print("\n Resuming with change %s" % change); + + if self.verbose: + print "parent determined through known branches: %s" % parent branch = self.gitRefForBranch(branch) parent = self.gitRefForBranch(parent) -- cgit v1.2.3 From ea09ea22d65d118328642e03ad23c8257304499d Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Wed, 5 Sep 2007 23:33:41 -0400 Subject: Don't allow contrib/workdir/git-new-workdir to trash existing dirs Recently I found that doing a sequence like the following: git-new-workdir a b ... git-new-workdir a b by accident will cause a (and now also b) to have an infinite cycle in its refs directory. This is caused by git-new-workdir trying to create the "refs" symlink over again, only during the second time it is being created within a's refs directory and is now also pointing back at a's refs. This causes confusion in git as suddenly branches are named things like "refs/refs/refs/refs/refs/refs/refs/heads/foo" instead of the more commonly accepted "refs/heads/foo". Plenty of commands start to see ambiguous ref names and others just take ages to compute. git-clone has the same safety check, so git-new-workdir should behave just like it. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index c6e154a84f..2838546d16 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -48,6 +48,12 @@ then "a complete repository." fi +# don't recreate a workdir over an existing repository +if test -e "$new_workdir" +then + die "destination directory '$new_workdir' already exists." +fi + # make sure the the links use full paths git_dir=$(cd "$git_dir"; pwd) -- cgit v1.2.3 From 0e5a7faa3a903cf7a0a66c81e20a76b91f17faab Mon Sep 17 00:00:00 2001 From: Carlos Rica Date: Tue, 11 Sep 2007 05:19:34 +0200 Subject: Make "git reset" a builtin. This replaces the script "git-reset.sh" with "builtin-reset.c". A few git commands used in the script are called from the builtin also: "ls-files" to check for unmerged files, "read-tree" for resetting the index file in "mixed" and "hard" resets, and "update-index" to refresh at the end in the "mixed" reset and also for the option that gets selected paths into the index. The reset option with paths was implemented by Johannes Schindelin. Since the option that gets selected paths into the index is not a "reset" like the others because it does not change the HEAD at all, now the command is showing a warning when the "--mixed" option is supplied for that purpose. The following table shows the behaviour of "git reset" for the different supported options, where X means "changing" the HEAD, index or working tree: reset: --soft --mixed --hard -- HEAD X X X - index - X X X files - - X - Signed-off-by: Carlos Rica Signed-off-by: Junio C Hamano --- contrib/examples/git-reset.sh | 106 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100755 contrib/examples/git-reset.sh (limited to 'contrib') diff --git a/contrib/examples/git-reset.sh b/contrib/examples/git-reset.sh new file mode 100755 index 0000000000..1dc606fbd3 --- /dev/null +++ b/contrib/examples/git-reset.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# +# Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano +# +USAGE='[--mixed | --soft | --hard] [] [ [--] ...]' +SUBDIRECTORY_OK=Yes +. git-sh-setup +set_reflog_action "reset $*" +require_work_tree + +update= reset_type=--mixed +unset rev + +while case $# in 0) break ;; esac +do + case "$1" in + --mixed | --soft | --hard) + reset_type="$1" + ;; + --) + break + ;; + -*) + usage + ;; + *) + rev=$(git rev-parse --verify "$1") || exit + shift + break + ;; + esac + shift +done + +: ${rev=HEAD} +rev=$(git rev-parse --verify $rev^0) || exit + +# Skip -- in "git reset HEAD -- foo" and "git reset -- foo". +case "$1" in --) shift ;; esac + +# git reset --mixed tree [--] paths... can be used to +# load chosen paths from the tree into the index without +# affecting the working tree nor HEAD. +if test $# != 0 +then + test "$reset_type" = "--mixed" || + die "Cannot do partial $reset_type reset." + + git diff-index --cached $rev -- "$@" | + sed -e 's/^:\([0-7][0-7]*\) [0-7][0-7]* \([0-9a-f][0-9a-f]*\) [0-9a-f][0-9a-f]* [A-Z] \(.*\)$/\1 \2 \3/' | + git update-index --add --remove --index-info || exit + git update-index --refresh + exit +fi + +cd_to_toplevel + +if test "$reset_type" = "--hard" +then + update=-u +fi + +# Soft reset does not touch the index file nor the working tree +# at all, but requires them in a good order. Other resets reset +# the index file to the tree object we are switching to. +if test "$reset_type" = "--soft" +then + if test -f "$GIT_DIR/MERGE_HEAD" || + test "" != "$(git ls-files --unmerged)" + then + die "Cannot do a soft reset in the middle of a merge." + fi +else + git read-tree -v --reset $update "$rev" || exit +fi + +# Any resets update HEAD to the head being switched to. +if orig=$(git rev-parse --verify HEAD 2>/dev/null) +then + echo "$orig" >"$GIT_DIR/ORIG_HEAD" +else + rm -f "$GIT_DIR/ORIG_HEAD" +fi +git update-ref -m "$GIT_REFLOG_ACTION" HEAD "$rev" +update_ref_status=$? + +case "$reset_type" in +--hard ) + test $update_ref_status = 0 && { + printf "HEAD is now at " + GIT_PAGER= git log --max-count=1 --pretty=oneline \ + --abbrev-commit HEAD + } + ;; +--soft ) + ;; # Nothing else to do +--mixed ) + # Report what has not been updated. + git update-index --refresh + ;; +esac + +rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" \ + "$GIT_DIR/SQUASH_MSG" "$GIT_DIR/MERGE_MSG" + +exit $update_ref_status -- cgit v1.2.3 From 1b655040bec1ff2f44fe309ac0f26085ed6372e9 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 13 Sep 2007 11:49:40 +0200 Subject: git.el: Keep the status buffer sorted by filename. This makes insertions and updates much more efficient. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 103 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 38 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 280557ecd4..b5d7c0664e 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -479,6 +479,27 @@ and returns the process output as a string." (setf (git-fileinfo->orig-name info) nil) (setf (git-fileinfo->needs-refresh info) t)))) +(defun git-set-filenames-state (status files state) + "Set the state of a list of named files." + (when files + (setq files (sort files #'string-lessp)) + (let ((file (pop files)) + (node (ewoc-nth status 0))) + (while (and file node) + (let ((info (ewoc-data node))) + (cond ((string-lessp (git-fileinfo->name info) file) + (setq node (ewoc-next status node))) + ((string-equal (git-fileinfo->name info) file) + (unless (eq (git-fileinfo->state info) state) + (setf (git-fileinfo->state info) state) + (setf (git-fileinfo->rename-state info) nil) + (setf (git-fileinfo->orig-name info) nil) + (setf (git-fileinfo->needs-refresh info) t)) + (setq file (pop files))) + (t (setq file (pop files))))))) + (unless state ;; delete files whose state has been set to nil + (ewoc-filter status (lambda (info) (git-fileinfo->state info)))))) + (defun git-state-code (code) "Convert from a string to a added/deleted/modified state." (case (string-to-char code) @@ -532,19 +553,36 @@ and returns the process output as a string." " " (git-escape-file-name (git-fileinfo->name info)) (git-rename-as-string info)))) -(defun git-insert-fileinfo (status info &optional refresh) - "Insert INFO in the status buffer, optionally refreshing an existing one." - (let ((node (and refresh - (git-find-status-file status (git-fileinfo->name info))))) - (setf (git-fileinfo->needs-refresh info) t) - (when node ;preserve the marked flag - (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))) - (if node (setf (ewoc-data node) info) (ewoc-enter-last status info)))) +(defun git-insert-info-list (status infolist) + "Insert a list of file infos in the status buffer, replacing existing ones if any." + (setq infolist (sort infolist + (lambda (info1 info2) + (string-lessp (git-fileinfo->name info1) + (git-fileinfo->name info2))))) + (let ((info (pop infolist)) + (node (ewoc-nth status 0))) + (while info + (setf (git-fileinfo->needs-refresh info) t) + (cond ((not node) + (ewoc-enter-last status info) + (setq info (pop infolist))) + ((string-lessp (git-fileinfo->name (ewoc-data node)) + (git-fileinfo->name info)) + (setq node (ewoc-next status node))) + ((string-equal (git-fileinfo->name (ewoc-data node)) + (git-fileinfo->name info)) + ;; preserve the marked flag + (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))) + (setf (ewoc-data node) info) + (setq info (pop infolist))) + (t + (ewoc-enter-before status node info) + (setq info (pop infolist))))))) (defun git-run-diff-index (status files) "Run git-diff-index on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let ((refresh files)) + (let (infolist) (with-temp-buffer (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) @@ -558,13 +596,14 @@ Return the list of files that haven't been handled." (new-name (match-string 8))) (if new-name ; copy or rename (if (eq ?C (string-to-char state)) - (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) refresh) - (git-insert-fileinfo status (git-create-fileinfo 'deleted name 0 0 'rename new-name) refresh) - (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)) refresh) - (git-insert-fileinfo status (git-create-fileinfo (git-state-code state) name old-perm new-perm) refresh)) + (push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist) + (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist) + (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist)) + (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist)) (setq files (delete name files)) - (when new-name (setq files (delete new-name files))))))) - files) + (when new-name (setq files (delete new-name files)))))) + (git-insert-info-list status infolist) + files)) (defun git-find-status-file (status file) "Find a given file in the status ewoc and return its node." @@ -576,16 +615,16 @@ Return the list of files that haven't been handled." (defun git-run-ls-files (status files default-state &rest options) "Run git-ls-files on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let ((refresh files)) + (let (infolist) (with-temp-buffer - (apply #'git-run-command t nil "ls-files" "-z" "-t" (append options (list "--") files)) + (apply #'git-run-command t nil "ls-files" "-z" (append options (list "--") files)) (goto-char (point-min)) - (while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1) - (let ((state (match-string 1)) - (name (match-string 2))) - (git-insert-fileinfo status (git-create-fileinfo (or (git-state-code state) default-state) name) refresh) - (setq files (delete name files)))))) - files) + (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) + (let ((name (match-string 1))) + (push (git-create-fileinfo default-state name) infolist) + (setq files (delete name files))))) + (git-insert-info-list status infolist) + files)) (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." @@ -594,9 +633,8 @@ Return the list of files that haven't been handled." (goto-char (point-min)) (let (unmerged-files) (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) - (let ((node (git-find-status-file status (match-string 1)))) - (when node (push (ewoc-data node) unmerged-files)))) - (git-set-files-state unmerged-files 'unmerged)))) + (push (match-string 1) unmerged-files)) + (git-set-filenames-state status unmerged-files 'unmerged)))) (defun git-get-exclude-files () "Get the list of exclude files to pass to git-ls-files." @@ -622,18 +660,7 @@ Return the list of files that haven't been handled." (setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o" (concat "--exclude-per-directory=" git-per-dir-ignore-file) (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) - ; mark remaining files with the default state (or remove them if nil) - (when remaining-files - (if default-state - (ewoc-map (lambda (info) - (when (member (git-fileinfo->name info) remaining-files) - (git-set-files-state (list info) default-state)) - nil) - status) - (ewoc-filter status - (lambda (info files) - (not (member (git-fileinfo->name info) files))) - remaining-files))) + (git-set-filenames-state status remaining-files default-state) (git-refresh-files) (git-refresh-ewoc-hf status))) -- cgit v1.2.3 From 98acc3fabc2d197525f1b0119382a00a664014b7 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 13 Sep 2007 11:50:08 +0200 Subject: git.el: Allow selecting whether to display uptodate/unknown/ignored files. The default behavior for each state can be customized, and it can also be toggled directly from the status buffer. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 92 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 15 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index b5d7c0664e..d1068c6258 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -97,6 +97,21 @@ if there is already one that displays the same directory." :group 'git :type 'string) +(defcustom git-show-uptodate nil + "Whether to display up-to-date files." + :group 'git + :type 'boolean) + +(defcustom git-show-ignored nil + "Whether to display ignored files." + :group 'git + :type 'boolean) + +(defcustom git-show-unknown t + "Whether to display unknown files." + :group 'git + :type 'boolean) + (defface git-status-face '((((class color) (background light)) (:foreground "purple")) @@ -646,23 +661,30 @@ Return the list of files that haven't been handled." (push config files)) files)) +(defun git-run-ls-files-with-excludes (status files default-state &rest options) + "Run git-ls-files on FILES with appropriate --exclude-from options." + (let ((exclude-files (git-get-exclude-files))) + (apply #'git-run-ls-files status files default-state + (concat "--exclude-per-directory=" git-per-dir-ignore-file) + (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) + (defun git-update-status-files (files &optional default-state) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) - (let* ((status git-status) - (remaining-files + (unless files + (when git-show-uptodate (git-run-ls-files git-status nil 'uptodate "-c"))) + (let* ((remaining-files (if (git-empty-db-p) ; we need some special handling for an empty db - (git-run-ls-files status files 'added "-c") - (git-run-diff-index status files)))) - (git-run-ls-unmerged status files) - (when (or (not files) remaining-files) - (let ((exclude-files (git-get-exclude-files))) - (setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o" - (concat "--exclude-per-directory=" git-per-dir-ignore-file) - (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) - (git-set-filenames-state status remaining-files default-state) + (git-run-ls-files git-status files 'added "-c") + (git-run-diff-index git-status files)))) + (git-run-ls-unmerged git-status files) + (when (or remaining-files (and git-show-unknown (not files))) + (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o"))) + (when (or remaining-files (and git-show-ignored (not files))) + (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i"))) + (git-set-filenames-state git-status remaining-files default-state) (git-refresh-files) - (git-refresh-ewoc-hf status))) + (git-refresh-ewoc-hf git-status))) (defun git-marked-files () "Return a list of all marked files, or if none a list containing just the file at cursor position." @@ -943,11 +965,41 @@ Return the list of files that haven't been handled." (interactive) (ewoc-filter git-status (lambda (info) - (not (or (eq (git-fileinfo->state info) 'ignored) - (eq (git-fileinfo->state info) 'uptodate))))) + (case (git-fileinfo->state info) + ('ignored git-show-ignored) + ('uptodate git-show-uptodate) + ('unknown git-show-unknown) + (t t)))) (unless (ewoc-nth git-status 0) ; refresh header if list is empty (git-refresh-ewoc-hf git-status))) +(defun git-toggle-show-uptodate () + "Toogle the option for showing up-to-date files." + (interactive) + (if (setq git-show-uptodate (not git-show-uptodate)) + (git-refresh-status) + (git-remove-handled))) + +(defun git-toggle-show-ignored () + "Toogle the option for showing ignored files." + (interactive) + (if (setq git-show-ignored (not git-show-ignored)) + (progn + (git-run-ls-files-with-excludes git-status nil 'ignored "-o" "-i") + (git-refresh-files) + (git-refresh-ewoc-hf git-status)) + (git-remove-handled))) + +(defun git-toggle-show-unknown () + "Toogle the option for showing unknown files." + (interactive) + (if (setq git-show-unknown (not git-show-unknown)) + (progn + (git-run-ls-files-with-excludes git-status nil 'unknown "-o") + (git-refresh-files) + (git-refresh-ewoc-hf git-status)) + (git-remove-handled))) + (defun git-setup-diff-buffer (buffer) "Setup a buffer for displaying a diff." (let ((dir default-directory)) @@ -1173,7 +1225,8 @@ Return the list of files that haven't been handled." (unless git-status-mode-map (let ((map (make-keymap)) - (diff-map (make-sparse-keymap))) + (diff-map (make-sparse-keymap)) + (toggle-map (make-sparse-keymap))) (suppress-keymap map) (define-key map "?" 'git-help) (define-key map "h" 'git-help) @@ -1197,6 +1250,7 @@ Return the list of files that haven't been handled." (define-key map "q" 'git-status-quit) (define-key map "r" 'git-remove-file) (define-key map "R" 'git-resolve-file) + (define-key map "t" toggle-map) (define-key map "T" 'git-toggle-all-marks) (define-key map "u" 'git-unmark-file) (define-key map "U" 'git-revert-file) @@ -1213,6 +1267,11 @@ Return the list of files that haven't been handled." (define-key diff-map "h" 'git-diff-file-merge-head) (define-key diff-map "m" 'git-diff-file-mine) (define-key diff-map "o" 'git-diff-file-other) + ; the toggle submap + (define-key toggle-map "u" 'git-toggle-show-uptodate) + (define-key toggle-map "i" 'git-toggle-show-ignored) + (define-key toggle-map "k" 'git-toggle-show-unknown) + (define-key toggle-map "m" 'git-toggle-all-marks) (setq git-status-mode-map map))) ;; git mode should only run in the *git status* buffer @@ -1234,6 +1293,9 @@ Commands: (let ((status (ewoc-create 'git-fileinfo-prettyprint "" ""))) (set (make-local-variable 'git-status) status)) (set (make-local-variable 'list-buffers-directory) default-directory) + (make-local-variable 'git-show-uptodate) + (make-local-variable 'git-show-ignored) + (make-local-variable 'git-show-unknown) (run-hooks 'git-status-mode-hook))) (defun git-find-status-buffer (dir) -- cgit v1.2.3 From 568d2cde9b9fe0fc6b3202c7987b13289cb1a4a0 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 13 Sep 2007 11:50:29 +0200 Subject: git.el: Allow the add and remove commands to be applied to ignored files. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d1068c6258..2d77fd47ec 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -902,7 +902,7 @@ Return the list of files that haven't been handled." (defun git-add-file () "Add marked file(s) to the index cache." (interactive) - (let ((files (git-get-filenames (git-marked-files-state 'unknown)))) + (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored)))) (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) (apply #'git-run-command nil nil "update-index" "--add" "--" files) @@ -920,7 +920,7 @@ Return the list of files that haven't been handled." (defun git-remove-file () "Remove the marked file(s)." (interactive) - (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate)))) + (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate 'ignored)))) (unless files (push (file-relative-name (read-file-name "File to remove: " nil nil t)) files)) (if (yes-or-no-p -- cgit v1.2.3 From 7f8cfadf218c8b28caf52b1490fb8b881945b0ea Mon Sep 17 00:00:00 2001 From: Nguyen Thai Ngoc Duy Date: Tue, 18 Sep 2007 03:26:01 -0400 Subject: contrib/fast-import: add simple shell example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This example just puts a directory under git control. It is significantly slower than using the git tools directly, but hopefully shows a bit how fast-import works. [jk: added header comments] Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- contrib/fast-import/git-import.sh | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 contrib/fast-import/git-import.sh (limited to 'contrib') diff --git a/contrib/fast-import/git-import.sh b/contrib/fast-import/git-import.sh new file mode 100755 index 0000000000..0ca7718d05 --- /dev/null +++ b/contrib/fast-import/git-import.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# +# Performs an initial import of a directory. This is the equivalent +# of doing 'git init; git add .; git commit'. It's a lot slower, +# but is meant to be a simple fast-import example. + +if [ -z "$1" -o -z "$2" ]; then + echo "Usage: git-import branch import-message" + exit 1 +fi + +USERNAME="$(git config user.name)" +EMAIL="$(git config user.email)" + +if [ -z "$USERNAME" -o -z "$EMAIL" ]; then + echo "You need to set user name and email" + exit 1 +fi + +git init + +( + cat < now +data < Date: Tue, 18 Sep 2007 03:26:27 -0400 Subject: contrib/fast-import: add perl version of simple example This is based on the git-import.sh script, but is a little more robust and efficient. More importantly, it should serve as a quick template for interfacing fast-import with perl scripts. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- contrib/fast-import/git-import.perl | 64 +++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100755 contrib/fast-import/git-import.perl (limited to 'contrib') diff --git a/contrib/fast-import/git-import.perl b/contrib/fast-import/git-import.perl new file mode 100755 index 0000000000..f9fef6db28 --- /dev/null +++ b/contrib/fast-import/git-import.perl @@ -0,0 +1,64 @@ +#!/usr/bin/perl +# +# Performs an initial import of a directory. This is the equivalent +# of doing 'git init; git add .; git commit'. It's a little slower, +# but is meant to be a simple fast-import example. + +use strict; +use File::Find; + +my $USAGE = 'Usage: git-import branch import-message'; +my $branch = shift or die "$USAGE\n"; +my $message = shift or die "$USAGE\n"; + +chomp(my $username = `git config user.name`); +chomp(my $email = `git config user.email`); +die 'You need to set user name and email' + unless $username && $email; + +system('git init'); +open(my $fi, '|-', qw(git fast-import --date-format=now)) + or die "unable to spawn fast-import: $!"; + +print $fi < now +data < 0) { + my $r = read($in, my $buf, $len < 4096 ? $len : 4096); + defined($r) or die "read error from $fn: $!"; + $r > 0 or die "premature EOF from $fn: $!"; + print $fi $buf; + $len -= $r; + } + print $fi "\n"; + + }, '.' +); + +close($fi); +exit $?; -- cgit v1.2.3 From af6fb4c822a5a65c7671d810127171759dff38f6 Mon Sep 17 00:00:00 2001 From: Josh England Date: Tue, 11 Sep 2007 10:59:04 -0600 Subject: Added example hook script to save/restore permissions/ownership. Usage info is emebed in the script, but the gist of it is to run the script from a pre-commit hook to save permissions/ownership data to a file and check that file into the repository. Then, a post_merge hook reads the file and updates working tree permissions/ownership. All updates are transparent to the user (although there is a --verbose option). Merge conflicts are handled in the "read" phase (in pre-commit), and the script aborts the commit and tells you how to fix things in the case of a merge conflict in the metadata file. This same idea could be extended to handle file ACLs or other file metadata if desired. Signed-off-by: Josh England Signed-off-by: Junio C Hamano --- contrib/hooks/setgitperms.perl | 213 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 contrib/hooks/setgitperms.perl (limited to 'contrib') diff --git a/contrib/hooks/setgitperms.perl b/contrib/hooks/setgitperms.perl new file mode 100644 index 0000000000..5e3b89def2 --- /dev/null +++ b/contrib/hooks/setgitperms.perl @@ -0,0 +1,213 @@ +#!/usr/bin/perl +# +# Copyright (c) 2006 Josh England +# +# This script can be used to save/restore full permissions and ownership data +# within a git working tree. +# +# To save permissions/ownership data, place this script in your .git/hooks +# directory and enable a `pre-commit` hook with the following lines: +# #!/bin/sh +# . git-sh-setup +# $GIT_DIR/hooks/setgitperms.perl -r +# +# To restore permissions/ownership data, place this script in your .git/hooks +# directory and enable a `post-merge` hook with the following lines: +# #!/bin/sh +# . git-sh-setup +# $GIT_DIR/hooks/setgitperms.perl -w +# +use strict; +use Getopt::Long; +use File::Find; +use File::Basename; + +my $usage = +"Usage: setgitperms.perl [OPTION]... <--read|--write> +This program uses a file `.gitmeta` to store/restore permissions and uid/gid +info for all files/dirs tracked by git in the repository. + +---------------------------------Read Mode------------------------------------- +-r, --read Reads perms/etc from working dir into a .gitmeta file +-s, --stdout Output to stdout instead of .gitmeta +-d, --diff Show unified diff of perms file (XOR with --stdout) + +---------------------------------Write Mode------------------------------------ +-w, --write Modify perms/etc in working dir to match the .gitmeta file +-v, --verbose Be verbose + +\n"; + +my ($stdout, $showdiff, $verbose, $read_mode, $write_mode); + +if ((@ARGV < 0) || !GetOptions( + "stdout", \$stdout, + "diff", \$showdiff, + "read", \$read_mode, + "write", \$write_mode, + "verbose", \$verbose, + )) { die $usage; } +die $usage unless ($read_mode xor $write_mode); + +my $topdir = `git-rev-parse --show-cdup` or die "\n"; chomp $topdir; +my $gitdir = $topdir . '.git'; +my $gitmeta = $topdir . '.gitmeta'; + +if ($write_mode) { + # Update the working dir permissions/ownership based on data from .gitmeta + open (IN, "<$gitmeta") or die "Could not open $gitmeta for reading: $!\n"; + while (defined ($_ = )) { + chomp; + if (/^(.*) mode=(\S+)\s+uid=(\d+)\s+gid=(\d+)/) { + # Compare recorded perms to actual perms in the working dir + my ($path, $mode, $uid, $gid) = ($1, $2, $3, $4); + my $fullpath = $topdir . $path; + my (undef,undef,$wmode,undef,$wuid,$wgid) = lstat($fullpath); + $wmode = sprintf "%04o", $wmode & 07777; + if ($mode ne $wmode) { + $verbose && print "Updating permissions on $path: old=$wmode, new=$mode\n"; + chmod oct($mode), $fullpath; + } + if ($uid != $wuid || $gid != $wgid) { + if ($verbose) { + # Print out user/group names instead of uid/gid + my $pwname = getpwuid($uid); + my $grpname = getgrgid($gid); + my $wpwname = getpwuid($wuid); + my $wgrpname = getgrgid($wgid); + $pwname = $uid if !defined $pwname; + $grpname = $gid if !defined $grpname; + $wpwname = $wuid if !defined $wpwname; + $wgrpname = $wgid if !defined $wgrpname; + + print "Updating uid/gid on $path: old=$wpwname/$wgrpname, new=$pwname/$grpname\n"; + } + chown $uid, $gid, $fullpath; + } + } + else { + warn "Invalid input format in $gitmeta:\n\t$_\n"; + } + } + close IN; +} +elsif ($read_mode) { + # Handle merge conflicts in the .gitperms file + if (-e "$gitdir/MERGE_MSG") { + if (`grep ====== $gitmeta`) { + # Conflict not resolved -- abort the commit + print "PERMISSIONS/OWNERSHIP CONFLICT\n"; + print " Resolve the conflict in the $gitmeta file and then run\n"; + print " `.git/hooks/setgitperms.perl --write` to reconcile.\n"; + exit 1; + } + elsif (`grep $gitmeta $gitdir/MERGE_MSG`) { + # A conflict in .gitmeta has been manually resolved. Verify that + # the working dir perms matches the current .gitmeta perms for + # each file/dir that conflicted. + # This is here because a `setgitperms.perl --write` was not + # performed due to a merge conflict, so permissions/ownership + # may not be consistent with the manually merged .gitmeta file. + my @conflict_diff = `git show \$(cat $gitdir/MERGE_HEAD)`; + my @conflict_files; + my $metadiff = 0; + + # Build a list of files that conflicted from the .gitmeta diff + foreach my $line (@conflict_diff) { + if ($line =~ m|^diff --git a/$gitmeta b/$gitmeta|) { + $metadiff = 1; + } + elsif ($line =~ /^diff --git/) { + $metadiff = 0; + } + elsif ($metadiff && $line =~ /^\+(.*) mode=/) { + push @conflict_files, $1; + } + } + + # Verify that each conflict file now has permissions consistent + # with the .gitmeta file + foreach my $file (@conflict_files) { + my $absfile = $topdir . $file; + my $gm_entry = `grep "^$file mode=" $gitmeta`; + if ($gm_entry =~ /mode=(\d+) uid=(\d+) gid=(\d+)/) { + my ($gm_mode, $gm_uid, $gm_gid) = ($1, $2, $3); + my (undef,undef,$mode,undef,$uid,$gid) = lstat("$absfile"); + $mode = sprintf("%04o", $mode & 07777); + if (($gm_mode ne $mode) || ($gm_uid != $uid) + || ($gm_gid != $gid)) { + print "PERMISSIONS/OWNERSHIP CONFLICT\n"; + print " Mismatch found for file: $file\n"; + print " Run `.git/hooks/setgitperms.perl --write` to reconcile.\n"; + exit 1; + } + } + else { + print "Warning! Permissions/ownership no longer being tracked for file: $file\n"; + } + } + } + } + + # No merge conflicts -- write out perms/ownership data to .gitmeta file + unless ($stdout) { + open (OUT, ">$gitmeta.tmp") or die "Could not open $gitmeta.tmp for writing: $!\n"; + } + + my @files = `git-ls-files`; + my %dirs; + + foreach my $path (@files) { + chomp $path; + # We have to manually add stats for parent directories + my $parent = dirname($path); + while (!exists $dirs{$parent}) { + $dirs{$parent} = 1; + next if $parent eq '.'; + printstats($parent); + $parent = dirname($parent); + } + # Now the git-tracked file + printstats($path); + } + + # diff the temporary metadata file to see if anything has changed + # If no metadata has changed, don't overwrite the real file + # This is just so `git commit -a` doesn't try to commit a bogus update + unless ($stdout) { + if (! -e $gitmeta) { + rename "$gitmeta.tmp", $gitmeta; + } + else { + my $diff = `diff -U 0 $gitmeta $gitmeta.tmp`; + if ($diff ne '') { + rename "$gitmeta.tmp", $gitmeta; + } + else { + unlink "$gitmeta.tmp"; + } + if ($showdiff) { + print $diff; + } + } + close OUT; + } + # Make sure the .gitmeta file is tracked + system("git add $gitmeta"); +} + + +sub printstats { + my $path = $_[0]; + $path =~ s/@/\@/g; + my (undef,undef,$mode,undef,$uid,$gid) = lstat($path); + $path =~ s/%/\%/g; + if ($stdout) { + print $path; + printf " mode=%04o uid=$uid gid=$gid\n", $mode & 07777; + } + else { + print OUT $path; + printf OUT " mode=%04o uid=$uid gid=$gid\n", $mode & 07777; + } +} -- cgit v1.2.3 From b888d61c8308027433df9c243fa551f42db1c76a Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Mon, 10 Sep 2007 23:03:25 -0400 Subject: Make fetch a builtin Thanks to Johannes Schindelin for review and fixes, and Julian Phillips for the original C translation. This changes a few small bits of behavior: branch..merge is parsed as if it were the lhs of a fetch refspec, and does not have to exactly match the actual lhs of a refspec, so long as it is a valid abbreviation for the same ref. branch..merge is no longer ignored if the remote is configured with a branches/* file. Neither behavior is useful, because there can only be one ref that gets fetched, but this is more consistant. Also, fetch prints different information to standard out. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- contrib/examples/git-fetch.sh | 377 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100755 contrib/examples/git-fetch.sh (limited to 'contrib') diff --git a/contrib/examples/git-fetch.sh b/contrib/examples/git-fetch.sh new file mode 100755 index 0000000000..c3a200120d --- /dev/null +++ b/contrib/examples/git-fetch.sh @@ -0,0 +1,377 @@ +#!/bin/sh +# + +USAGE=' ...' +SUBDIRECTORY_OK=Yes +. git-sh-setup +set_reflog_action "fetch $*" +cd_to_toplevel ;# probably unnecessary... + +. git-parse-remote +_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' +_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" + +LF=' +' +IFS="$LF" + +no_tags= +tags= +append= +force= +verbose= +update_head_ok= +exec= +keep= +shallow_depth= +no_progress= +test -t 1 || no_progress=--no-progress +quiet= +while case "$#" in 0) break ;; esac +do + case "$1" in + -a|--a|--ap|--app|--appe|--appen|--append) + append=t + ;; + --upl|--uplo|--uploa|--upload|--upload-|--upload-p|\ + --upload-pa|--upload-pac|--upload-pack) + shift + exec="--upload-pack=$1" + ;; + --upl=*|--uplo=*|--uploa=*|--upload=*|\ + --upload-=*|--upload-p=*|--upload-pa=*|--upload-pac=*|--upload-pack=*) + exec=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)') + shift + ;; + -f|--f|--fo|--for|--forc|--force) + force=t + ;; + -t|--t|--ta|--tag|--tags) + tags=t + ;; + -n|--n|--no|--no-|--no-t|--no-ta|--no-tag|--no-tags) + no_tags=t + ;; + -u|--u|--up|--upd|--upda|--updat|--update|--update-|--update-h|\ + --update-he|--update-hea|--update-head|--update-head-|\ + --update-head-o|--update-head-ok) + update_head_ok=t + ;; + -q|--q|--qu|--qui|--quie|--quiet) + quiet=--quiet + ;; + -v|--verbose) + verbose="$verbose"Yes + ;; + -k|--k|--ke|--kee|--keep) + keep='-k -k' + ;; + --depth=*) + shallow_depth="--depth=`expr "z$1" : 'z-[^=]*=\(.*\)'`" + ;; + --depth) + shift + shallow_depth="--depth=$1" + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift +done + +case "$#" in +0) + origin=$(get_default_remote) + test -n "$(get_remote_url ${origin})" || + die "Where do you want to fetch from today?" + set x $origin ; shift ;; +esac + +if test -z "$exec" +then + # No command line override and we have configuration for the remote. + exec="--upload-pack=$(get_uploadpack $1)" +fi + +remote_nick="$1" +remote=$(get_remote_url "$@") +refs= +rref= +rsync_slurped_objects= + +if test "" = "$append" +then + : >"$GIT_DIR/FETCH_HEAD" +fi + +# Global that is reused later +ls_remote_result=$(git ls-remote $exec "$remote") || + die "Cannot get the repository state from $remote" + +append_fetch_head () { + flags= + test -n "$verbose" && flags="$flags$LF-v" + test -n "$force$single_force" && flags="$flags$LF-f" + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \ + git fetch--tool $flags append-fetch-head "$@" +} + +# updating the current HEAD with git-fetch in a bare +# repository is always fine. +if test -z "$update_head_ok" && test $(is_bare_repository) = false +then + orig_head=$(git rev-parse --verify HEAD 2>/dev/null) +fi + +# Allow --notags from remote.$1.tagopt +case "$tags$no_tags" in +'') + case "$(git config --get "remote.$1.tagopt")" in + --no-tags) + no_tags=t ;; + esac +esac + +# If --tags (and later --heads or --all) is specified, then we are +# not talking about defaults stored in Pull: line of remotes or +# branches file, and just fetch those and refspecs explicitly given. +# Otherwise we do what we always did. + +reflist=$(get_remote_refs_for_fetch "$@") +if test "$tags" +then + taglist=`IFS=' ' && + echo "$ls_remote_result" | + git show-ref --exclude-existing=refs/tags/ | + while read sha1 name + do + echo ".${name}:${name}" + done` || exit + if test "$#" -gt 1 + then + # remote URL plus explicit refspecs; we need to merge them. + reflist="$reflist$LF$taglist" + else + # No explicit refspecs; fetch tags only. + reflist=$taglist + fi +fi + +fetch_all_at_once () { + + eval=$(echo "$1" | git fetch--tool parse-reflist "-") + eval "$eval" + + ( : subshell because we muck with IFS + IFS=" $LF" + ( + if test "$remote" = . ; then + git show-ref $rref || echo failed "$remote" + elif test -f "$remote" ; then + test -n "$shallow_depth" && + die "shallow clone with bundle is not supported" + git bundle unbundle "$remote" $rref || + echo failed "$remote" + else + 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=$(echo "$ls_remote_result" | \ + git fetch--tool -s pick-rref "$rref" "-") && + + # 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 + echo "$ls_remote_result" | \ + git fetch--tool pick-rref "$rref" "-" + else + flags= + case $verbose in + YesYes*) + flags="-v" + ;; + esac + git-fetch-pack --thin $exec $keep $shallow_depth \ + $quiet $no_progress $flags "$remote" $rref || + echo failed "$remote" + fi + fi + ) | + ( + flags= + test -n "$verbose" && flags="$flags -v" + test -n "$force" && flags="$flags -f" + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \ + git fetch--tool $flags native-store \ + "$remote" "$remote_nick" "$refs" + ) + ) || exit + +} + +fetch_per_ref () { + reflist="$1" + refs= + rref= + + for ref in $reflist + do + refs="$refs$LF$ref" + + # These are relative path from $GIT_DIR, typically starting at refs/ + # but may be HEAD + if expr "z$ref" : 'z\.' >/dev/null + then + not_for_merge=t + ref=$(expr "z$ref" : 'z\.\(.*\)') + else + not_for_merge= + fi + if expr "z$ref" : 'z+' >/dev/null + then + single_force=t + ref=$(expr "z$ref" : 'z+\(.*\)') + else + single_force= + fi + remote_name=$(expr "z$ref" : 'z\([^:]*\):') + local_name=$(expr "z$ref" : 'z[^:]*:\(.*\)') + + rref="$rref$LF$remote_name" + + # There are transports that can fetch only one head at a time... + case "$remote" in + http://* | https://* | ftp://*) + test -n "$shallow_depth" && + die "shallow clone with http not supported" + proto=`expr "$remote" : '\([^:]*\):'` + if [ -n "$GIT_SSL_NO_VERIFY" ]; then + curl_extra_args="-k" + fi + if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \ + "`git config --bool http.noEPSV`" = true ]; then + noepsv_opt="--disable-epsv" + fi + + # Find $remote_name from ls-remote output. + head=$(echo "$ls_remote_result" | \ + git fetch--tool -s pick-rref "$remote_name" "-") + expr "z$head" : "z$_x40\$" >/dev/null || + die "No such ref $remote_name at $remote" + echo >&2 "Fetching $remote_name from $remote using $proto" + case "$quiet" in '') v=-v ;; *) v= ;; esac + git-http-fetch $v -a "$head" "$remote" || exit + ;; + rsync://*) + test -n "$shallow_depth" && + die "shallow clone with rsync not supported" + TMP_HEAD="$GIT_DIR/TMP_HEAD" + rsync -L -q "$remote/$remote_name" "$TMP_HEAD" || exit 1 + head=$(git rev-parse --verify TMP_HEAD) + rm -f "$TMP_HEAD" + case "$quiet" in '') v=-v ;; *) v= ;; esac + test "$rsync_slurped_objects" || { + rsync -a $v --ignore-existing --exclude info \ + "$remote/objects/" "$GIT_OBJECT_DIRECTORY/" || exit + + # Look at objects/info/alternates for rsync -- http will + # support it natively and git native ones will do it on + # the remote end. Not having that file is not a crime. + rsync -q "$remote/objects/info/alternates" \ + "$GIT_DIR/TMP_ALT" 2>/dev/null || + rm -f "$GIT_DIR/TMP_ALT" + if test -f "$GIT_DIR/TMP_ALT" + then + resolve_alternates "$remote" <"$GIT_DIR/TMP_ALT" | + while read alt + do + case "$alt" in 'bad alternate: '*) die "$alt";; esac + echo >&2 "Getting alternate: $alt" + rsync -av --ignore-existing --exclude info \ + "$alt" "$GIT_OBJECT_DIRECTORY/" || exit + done + rm -f "$GIT_DIR/TMP_ALT" + fi + rsync_slurped_objects=t + } + ;; + esac + + append_fetch_head "$head" "$remote" \ + "$remote_name" "$remote_nick" "$local_name" "$not_for_merge" || exit + + done + +} + +fetch_main () { + case "$remote" in + http://* | https://* | ftp://* | rsync://* ) + fetch_per_ref "$@" + ;; + *) + fetch_all_at_once "$@" + ;; + esac +} + +fetch_main "$reflist" || exit + +# automated tag following +case "$no_tags$tags" in +'') + case "$reflist" in + *:refs/*) + # effective only when we are following remote branch + # using local tracking branch. + taglist=$(IFS=' ' && + echo "$ls_remote_result" | + git show-ref --exclude-existing=refs/tags/ | + while read sha1 name + do + git cat-file -t "$sha1" >/dev/null 2>&1 || continue + echo >&2 "Auto-following $name" + echo ".${name}:${name}" + done) + esac + case "$taglist" in + '') ;; + ?*) + # do not deepen a shallow tree when following tags + shallow_depth= + fetch_main "$taglist" || exit ;; + esac +esac + +# If the original head was empty (i.e. no "master" yet), or +# if we were told not to worry, we do not have to check. +case "$orig_head" in +'') + ;; +?*) + curr_head=$(git rev-parse --verify HEAD 2>/dev/null) + if test "$curr_head" != "$orig_head" + then + git update-ref \ + -m "$GIT_REFLOG_ACTION: Undoing incorrectly fetched HEAD." \ + HEAD "$orig_head" + die "Cannot fetch into the current branch." + fi + ;; +esac -- cgit v1.2.3 From b9fc6ea9efdc988d851666d45d80076839d9c225 Mon Sep 17 00:00:00 2001 From: David Brown Date: Wed, 19 Sep 2007 13:12:48 -0700 Subject: Detect exec bit in more cases. git-p4 was missing the execute bit setting if the file had other attribute bits set. Acked-By: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 55778c5775..65c57ac4d8 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -63,6 +63,14 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) +def isP4Exec(kind): + """Determine if a Perforce 'kind' should have execute permission + + 'p4 help filetypes' gives a list of the types. If it starts with 'x', + or x follows one of a few letters. Otherwise, if there is an 'x' after + a plus sign, it is also executable""" + return (re.search(r"(^[cku]?x)|\+.*x", kind) != None) + def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: @@ -916,7 +924,7 @@ class P4Sync(Command): data = file['data'] mode = "644" - if file["type"].startswith("x"): + if isP4Exec(file["type"]): mode = "755" elif file["type"] == "symlink": mode = "120000" -- cgit v1.2.3 From 822f7c7349d61f6075961ce42c1bd1a85cf999e5 Mon Sep 17 00:00:00 2001 From: David Kastrup Date: Sun, 23 Sep 2007 22:42:08 +0200 Subject: Supplant the "while case ... break ;; esac" idiom A lot of shell scripts contained stuff starting with while case "$#" in 0) break ;; esac and similar. I consider breaking out of the condition instead of the body od the loop ugly, and the implied "true" value of the non-matching case is not really obvious to humans at first glance. It happens not to be obvious to some BSD shells, either, but that's because they are not POSIX-compliant. In most cases, this has been replaced by a straight condition using "test". "case" has the advantage of being faster than "test" on vintage shells where "test" is not a builtin. Since none of them is likely to run the git scripts, anyway, the added readability should be worth the change. A few loops have had their termination condition expressed differently. Signed-off-by: David Kastrup Signed-off-by: Junio C Hamano --- contrib/examples/git-gc.sh | 2 +- contrib/examples/git-tag.sh | 2 +- contrib/examples/git-verify-tag.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-gc.sh b/contrib/examples/git-gc.sh index 2ae235b081..1597e9f33f 100755 --- a/contrib/examples/git-gc.sh +++ b/contrib/examples/git-gc.sh @@ -9,7 +9,7 @@ SUBDIRECTORY_OK=Yes . git-sh-setup no_prune=: -while case $# in 0) break ;; esac +while test $# != 0 do case "$1" in --prune) diff --git a/contrib/examples/git-tag.sh b/contrib/examples/git-tag.sh index 5ee3f50a3c..ae7c531666 100755 --- a/contrib/examples/git-tag.sh +++ b/contrib/examples/git-tag.sh @@ -14,7 +14,7 @@ username= list= verify= LINES=0 -while case "$#" in 0) break ;; esac +while test $# != 0 do case "$1" in -a) diff --git a/contrib/examples/git-verify-tag.sh b/contrib/examples/git-verify-tag.sh index 37b0023b27..0902a5c21a 100755 --- a/contrib/examples/git-verify-tag.sh +++ b/contrib/examples/git-verify-tag.sh @@ -5,7 +5,7 @@ SUBDIRECTORY_OK='Yes' . git-sh-setup verbose= -while case $# in 0) break;; esac +while test $# != 0 do case "$1" in -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) -- cgit v1.2.3 From d1637a07f684acd80007723f94c4da9649d85525 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Tue, 25 Sep 2007 08:48:59 +0200 Subject: Do not over-quote the -f envelopesender value. Without this, the value passed to sendmail would have an extra set of single quotes. At least exim's sendmail emulation would object to that: exim: bad -f address "'list-addr@example.org'": malformed address: ' \ may not follow 'list-addr@example.org error: hooks/post-receive exited with error code 1 Signed-off-by: Jim Meyering Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index c589a39a0c..1f88099df4 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -571,6 +571,15 @@ generate_delete_general_email() echo $LOGEND } +send_mail() +{ + if [ -n "$envelopesender" ]; then + /usr/sbin/sendmail -t -f "$envelopesender" + else + /usr/sbin/sendmail -t + fi +} + # ---------------------------- main() # --- Constants @@ -607,13 +616,8 @@ if [ -n "$1" -a -n "$2" -a -n "$3" ]; then # resend an email; they could redirect the output to sendmail themselves PAGER= generate_email $2 $3 $1 else - if [ -n "$envelopesender" ]; then - envelopesender="-f '$envelopesender'" - fi - while read oldrev newrev refname do - generate_email $oldrev $newrev $refname | - /usr/sbin/sendmail -t $envelopesender + generate_email $oldrev $newrev $refname | send_mail done fi -- cgit v1.2.3 From 2ecb5ea2ad375017eedf73bd0130fa9ca33010d2 Mon Sep 17 00:00:00 2001 From: Matt Kraai Date: Tue, 25 Sep 2007 07:03:46 -0700 Subject: Move convert-objects to contrib. convert-objects was needed to convert from an old-style repository, which hashed the compressed contents and used a different date format. Such repositories are presumably no longer common and, if such conversions are necessary, should be done by writing a frontend for git-fast-import. Linus, the original author, is OK with moving it to contrib. Signed-off-by: Matt Kraai Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 - contrib/convert-objects/convert-objects.c | 329 ++++++++++++++++++++++++ contrib/convert-objects/git-convert-objects.txt | 28 ++ 3 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 contrib/convert-objects/convert-objects.c create mode 100644 contrib/convert-objects/git-convert-objects.txt (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index cad842af45..e760930740 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -299,7 +299,6 @@ __git_commands () check-attr) : plumbing;; check-ref-format) : plumbing;; commit-tree) : plumbing;; - convert-objects) : plumbing;; cvsexportcommit) : export;; cvsimport) : import;; cvsserver) : daemon;; diff --git a/contrib/convert-objects/convert-objects.c b/contrib/convert-objects/convert-objects.c new file mode 100644 index 0000000000..90e7900e6d --- /dev/null +++ b/contrib/convert-objects/convert-objects.c @@ -0,0 +1,329 @@ +#include "cache.h" +#include "blob.h" +#include "commit.h" +#include "tree.h" + +struct entry { + unsigned char old_sha1[20]; + unsigned char new_sha1[20]; + int converted; +}; + +#define MAXOBJECTS (1000000) + +static struct entry *convert[MAXOBJECTS]; +static int nr_convert; + +static struct entry * convert_entry(unsigned char *sha1); + +static struct entry *insert_new(unsigned char *sha1, int pos) +{ + struct entry *new = xcalloc(1, sizeof(struct entry)); + hashcpy(new->old_sha1, sha1); + memmove(convert + pos + 1, convert + pos, (nr_convert - pos) * sizeof(struct entry *)); + convert[pos] = new; + nr_convert++; + if (nr_convert == MAXOBJECTS) + die("you're kidding me - hit maximum object limit"); + return new; +} + +static struct entry *lookup_entry(unsigned char *sha1) +{ + int low = 0, high = nr_convert; + + while (low < high) { + int next = (low + high) / 2; + struct entry *n = convert[next]; + int cmp = hashcmp(sha1, n->old_sha1); + if (!cmp) + return n; + if (cmp < 0) { + high = next; + continue; + } + low = next+1; + } + return insert_new(sha1, low); +} + +static void convert_binary_sha1(void *buffer) +{ + struct entry *entry = convert_entry(buffer); + hashcpy(buffer, entry->new_sha1); +} + +static void convert_ascii_sha1(void *buffer) +{ + unsigned char sha1[20]; + struct entry *entry; + + if (get_sha1_hex(buffer, sha1)) + die("expected sha1, got '%s'", (char*) buffer); + entry = convert_entry(sha1); + memcpy(buffer, sha1_to_hex(entry->new_sha1), 40); +} + +static unsigned int convert_mode(unsigned int mode) +{ + unsigned int newmode; + + newmode = mode & S_IFMT; + if (S_ISREG(mode)) + newmode |= (mode & 0100) ? 0755 : 0644; + return newmode; +} + +static int write_subdirectory(void *buffer, unsigned long size, const char *base, int baselen, unsigned char *result_sha1) +{ + char *new = xmalloc(size); + unsigned long newlen = 0; + unsigned long used; + + used = 0; + while (size) { + int len = 21 + strlen(buffer); + char *path = strchr(buffer, ' '); + unsigned char *sha1; + unsigned int mode; + char *slash, *origpath; + + if (!path || strtoul_ui(buffer, 8, &mode)) + die("bad tree conversion"); + mode = convert_mode(mode); + path++; + if (memcmp(path, base, baselen)) + break; + origpath = path; + path += baselen; + slash = strchr(path, '/'); + if (!slash) { + newlen += sprintf(new + newlen, "%o %s", mode, path); + new[newlen++] = '\0'; + hashcpy((unsigned char*)new + newlen, (unsigned char *) buffer + len - 20); + newlen += 20; + + used += len; + size -= len; + buffer = (char *) buffer + len; + continue; + } + + newlen += sprintf(new + newlen, "%o %.*s", S_IFDIR, (int)(slash - path), path); + new[newlen++] = 0; + sha1 = (unsigned char *)(new + newlen); + newlen += 20; + + len = write_subdirectory(buffer, size, origpath, slash-origpath+1, sha1); + + used += len; + size -= len; + buffer = (char *) buffer + len; + } + + write_sha1_file(new, newlen, tree_type, result_sha1); + free(new); + return used; +} + +static void convert_tree(void *buffer, unsigned long size, unsigned char *result_sha1) +{ + void *orig_buffer = buffer; + unsigned long orig_size = size; + + while (size) { + size_t len = 1+strlen(buffer); + + convert_binary_sha1((char *) buffer + len); + + len += 20; + if (len > size) + die("corrupt tree object"); + size -= len; + buffer = (char *) buffer + len; + } + + write_subdirectory(orig_buffer, orig_size, "", 0, result_sha1); +} + +static unsigned long parse_oldstyle_date(const char *buf) +{ + char c, *p; + char buffer[100]; + struct tm tm; + const char *formats[] = { + "%c", + "%a %b %d %T", + "%Z", + "%Y", + " %Y", + NULL + }; + /* We only ever did two timezones in the bad old format .. */ + const char *timezones[] = { + "PDT", "PST", "CEST", NULL + }; + const char **fmt = formats; + + p = buffer; + while (isspace(c = *buf)) + buf++; + while ((c = *buf++) != '\n') + *p++ = c; + *p++ = 0; + buf = buffer; + memset(&tm, 0, sizeof(tm)); + do { + const char *next = strptime(buf, *fmt, &tm); + if (next) { + if (!*next) + return mktime(&tm); + buf = next; + } else { + const char **p = timezones; + while (isspace(*buf)) + buf++; + while (*p) { + if (!memcmp(buf, *p, strlen(*p))) { + buf += strlen(*p); + break; + } + p++; + } + } + fmt++; + } while (*buf && *fmt); + printf("left: %s\n", buf); + return mktime(&tm); +} + +static int convert_date_line(char *dst, void **buf, unsigned long *sp) +{ + unsigned long size = *sp; + char *line = *buf; + char *next = strchr(line, '\n'); + char *date = strchr(line, '>'); + int len; + + if (!next || !date) + die("missing or bad author/committer line %s", line); + next++; date += 2; + + *buf = next; + *sp = size - (next - line); + + len = date - line; + memcpy(dst, line, len); + dst += len; + + /* Is it already in new format? */ + if (isdigit(*date)) { + int datelen = next - date; + memcpy(dst, date, datelen); + return len + datelen; + } + + /* + * Hacky hacky: one of the sparse old-style commits does not have + * any date at all, but we can fake it by using the committer date. + */ + if (*date == '\n' && strchr(next, '>')) + date = strchr(next, '>')+2; + + return len + sprintf(dst, "%lu -0700\n", parse_oldstyle_date(date)); +} + +static void convert_date(void *buffer, unsigned long size, unsigned char *result_sha1) +{ + char *new = xmalloc(size + 100); + unsigned long newlen = 0; + + /* "tree \n" */ + memcpy(new + newlen, buffer, 46); + newlen += 46; + buffer = (char *) buffer + 46; + size -= 46; + + /* "parent \n" */ + while (!memcmp(buffer, "parent ", 7)) { + memcpy(new + newlen, buffer, 48); + newlen += 48; + buffer = (char *) buffer + 48; + size -= 48; + } + + /* "author xyz date" */ + newlen += convert_date_line(new + newlen, &buffer, &size); + /* "committer xyz date" */ + newlen += convert_date_line(new + newlen, &buffer, &size); + + /* Rest */ + memcpy(new + newlen, buffer, size); + newlen += size; + + write_sha1_file(new, newlen, commit_type, result_sha1); + free(new); +} + +static void convert_commit(void *buffer, unsigned long size, unsigned char *result_sha1) +{ + void *orig_buffer = buffer; + unsigned long orig_size = size; + + if (memcmp(buffer, "tree ", 5)) + die("Bad commit '%s'", (char*) buffer); + convert_ascii_sha1((char *) buffer + 5); + buffer = (char *) buffer + 46; /* "tree " + "hex sha1" + "\n" */ + while (!memcmp(buffer, "parent ", 7)) { + convert_ascii_sha1((char *) buffer + 7); + buffer = (char *) buffer + 48; + } + convert_date(orig_buffer, orig_size, result_sha1); +} + +static struct entry * convert_entry(unsigned char *sha1) +{ + struct entry *entry = lookup_entry(sha1); + enum object_type type; + void *buffer, *data; + unsigned long size; + + if (entry->converted) + return entry; + data = read_sha1_file(sha1, &type, &size); + if (!data) + die("unable to read object %s", sha1_to_hex(sha1)); + + buffer = xmalloc(size); + memcpy(buffer, data, size); + + if (type == OBJ_BLOB) { + write_sha1_file(buffer, size, blob_type, entry->new_sha1); + } else if (type == OBJ_TREE) + convert_tree(buffer, size, entry->new_sha1); + else if (type == OBJ_COMMIT) + convert_commit(buffer, size, entry->new_sha1); + else + die("unknown object type %d in %s", type, sha1_to_hex(sha1)); + entry->converted = 1; + free(buffer); + free(data); + return entry; +} + +int main(int argc, char **argv) +{ + unsigned char sha1[20]; + struct entry *entry; + + setup_git_directory(); + + if (argc != 2) + usage("git-convert-objects "); + if (get_sha1(argv[1], sha1)) + die("Not a valid object name %s", argv[1]); + + entry = convert_entry(sha1); + printf("new sha1: %s\n", sha1_to_hex(entry->new_sha1)); + return 0; +} diff --git a/contrib/convert-objects/git-convert-objects.txt b/contrib/convert-objects/git-convert-objects.txt new file mode 100644 index 0000000000..9718abf86d --- /dev/null +++ b/contrib/convert-objects/git-convert-objects.txt @@ -0,0 +1,28 @@ +git-convert-objects(1) +====================== + +NAME +---- +git-convert-objects - Converts old-style git repository + + +SYNOPSIS +-------- +'git-convert-objects' + +DESCRIPTION +----------- +Converts old-style git repository to the latest format + + +Author +------ +Written by Linus Torvalds + +Documentation +-------------- +Documentation by David Greaves, Junio C Hamano and the git-list . + +GIT +--- +Part of the gitlink:git[7] suite -- cgit v1.2.3 From b9b7bab4b6190ef879bb72c7fabf879fd9ca852f Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 29 Sep 2007 11:58:08 +0200 Subject: git.el: Preserve file marks when doing a full refresh. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 2d77fd47ec..7b4a0d3602 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -494,24 +494,31 @@ and returns the process output as a string." (setf (git-fileinfo->orig-name info) nil) (setf (git-fileinfo->needs-refresh info) t)))) -(defun git-set-filenames-state (status files state) - "Set the state of a list of named files." +(defun git-status-filenames-map (status func files &rest args) + "Apply FUNC to the status files names in the FILES list." (when files (setq files (sort files #'string-lessp)) (let ((file (pop files)) (node (ewoc-nth status 0))) (while (and file node) (let ((info (ewoc-data node))) - (cond ((string-lessp (git-fileinfo->name info) file) - (setq node (ewoc-next status node))) - ((string-equal (git-fileinfo->name info) file) - (unless (eq (git-fileinfo->state info) state) - (setf (git-fileinfo->state info) state) - (setf (git-fileinfo->rename-state info) nil) - (setf (git-fileinfo->orig-name info) nil) - (setf (git-fileinfo->needs-refresh info) t)) - (setq file (pop files))) - (t (setq file (pop files))))))) + (if (string-lessp (git-fileinfo->name info) file) + (setq node (ewoc-next status node)) + (if (string-equal (git-fileinfo->name info) file) + (apply func info args)) + (setq file (pop files)))))))) + +(defun git-set-filenames-state (status files state) + "Set the state of a list of named files." + (when files + (git-status-filenames-map status + (lambda (info state) + (unless (eq (git-fileinfo->state info) state) + (setf (git-fileinfo->state info) state) + (setf (git-fileinfo->rename-state info) nil) + (setf (git-fileinfo->orig-name info) nil) + (setf (git-fileinfo->needs-refresh info) t))) + files state) (unless state ;; delete files whose state has been set to nil (ewoc-filter status (lambda (info) (git-fileinfo->state info)))))) @@ -1197,11 +1204,20 @@ Return the list of files that haven't been handled." (interactive) (let* ((status git-status) (pos (ewoc-locate status)) + (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info))))) (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) (unless status (error "Not in git-status buffer.")) (git-run-command nil nil "update-index" "--refresh") (git-clear-status status) (git-update-status-files nil) + ; restore file marks + (when marked-files + (git-status-filenames-map status + (lambda (info) + (setf (git-fileinfo->marked info) t) + (setf (git-fileinfo->needs-refresh info) t)) + marked-files) + (git-refresh-files)) ; move point to the current file name if any (let ((node (and cur-name (git-find-status-file status cur-name)))) (when node (ewoc-goto-node status node))))) -- cgit v1.2.3 From 9f5599b9829615379353f676369018c47296e1a1 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 29 Sep 2007 11:58:39 +0200 Subject: git.el: Do not print a status message on every git command. Instead print a single message around sequences of commands that can potentially take some time. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 70 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 29 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 7b4a0d3602..ec2e699061 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -220,22 +220,15 @@ and returns the process output as a string." (message "Running git %s...done" (car args)) buffer)) -(defun git-run-command (buffer env &rest args) - (message "Running git %s..." (car args)) - (apply #'git-call-process-env buffer env args) - (message "Running git %s...done" (car args))) - (defun git-run-command-region (buffer start end env &rest args) "Run a git command with specified buffer region as input." - (message "Running git %s..." (car args)) (unless (eq 0 (if env (git-run-process-region buffer start end "env" (append (git-get-env-strings env) (list "git") args)) (git-run-process-region buffer start end "git" args))) - (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))) - (message "Running git %s...done" (car args))) + (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))) (defun git-run-hook (hook env &rest args) "Run a git hook and display its output if any." @@ -312,6 +305,13 @@ and returns the process output as a string." "\"") name)) +(defun git-success-message (text files) + "Print a success message after having handled FILES." + (let ((n (length files))) + (if (equal n 1) + (message "%s %s" text (car files)) + (message "%s %d files" text n)))) + (defun git-get-top-dir (dir) "Retrieve the top-level directory of a git tree." (let ((cdup (with-output-to-string @@ -338,7 +338,7 @@ and returns the process output as a string." (sort-lines nil (point-min) (point-max)) (save-buffer)) (when created - (git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) + (git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) (git-update-status-files (list (file-relative-name ignore-name)) 'unknown))) ; propertize definition for XEmacs, stolen from erc-compat @@ -606,7 +606,7 @@ and returns the process output as a string." Return the list of files that haven't been handled." (let (infolist) (with-temp-buffer - (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files) + (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) (while (re-search-forward ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" @@ -639,7 +639,7 @@ Return the list of files that haven't been handled." Return the list of files that haven't been handled." (let (infolist) (with-temp-buffer - (apply #'git-run-command t nil "ls-files" "-z" (append options (list "--") files)) + (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files)) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (let ((name (match-string 1))) @@ -651,7 +651,7 @@ Return the list of files that haven't been handled." (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." (with-temp-buffer - (apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files) + (apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files) (goto-char (point-min)) (let (unmerged-files) (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) @@ -754,11 +754,11 @@ Return the list of files that haven't been handled." ('deleted (push info deleted)) ('modified (push info modified)))) (when added - (apply #'git-run-command nil env "update-index" "--add" "--" (git-get-filenames added))) + (apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added))) (when deleted - (apply #'git-run-command nil env "update-index" "--remove" "--" (git-get-filenames deleted))) + (apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted))) (when modified - (apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified))))) + (apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified))))) (defun git-run-pre-commit-hook () "Run the pre-commit hook if any." @@ -790,6 +790,7 @@ Return the list of files that haven't been handled." head-tree (git-rev-parse "HEAD^{tree}"))) (if files (progn + (message "Running git commit...") (git-read-tree head-tree index-file) (git-update-index nil files) ;update both the default index (git-update-index index-file files) ;and the temporary one @@ -801,7 +802,7 @@ Return the list of files that haven't been handled." (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) (git-set-files-state files 'uptodate) - (git-run-command nil nil "rerere") + (git-call-process-env nil nil "rerere") (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) @@ -912,8 +913,9 @@ Return the list of files that haven't been handled." (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored)))) (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) - (apply #'git-run-command nil nil "update-index" "--add" "--" files) - (git-update-status-files files 'uptodate))) + (apply #'git-call-process-env nil nil "update-index" "--add" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Added" files))) (defun git-ignore-file () "Add marked file(s) to the ignore list." @@ -922,7 +924,8 @@ Return the list of files that haven't been handled." (unless files (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files)) (dolist (f files) (git-append-to-ignore f)) - (git-update-status-files files 'ignored))) + (git-update-status-files files 'ignored) + (git-success-message "Ignored" files))) (defun git-remove-file () "Remove the marked file(s)." @@ -935,8 +938,9 @@ Return the list of files that haven't been handled." (progn (dolist (name files) (when (file-exists-p name) (delete-file name))) - (apply #'git-run-command nil nil "update-index" "--remove" "--" files) - (git-update-status-files files nil)) + (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files) + (git-update-status-files files nil) + (git-success-message "Removed" files)) (message "Aborting")))) (defun git-revert-file () @@ -954,18 +958,20 @@ Return the list of files that haven't been handled." ('unmerged (push (git-fileinfo->name info) modified)) ('modified (push (git-fileinfo->name info) modified)))) (when added - (apply #'git-run-command nil nil "update-index" "--force-remove" "--" added)) + (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added)) (when modified - (apply #'git-run-command nil nil "checkout" "HEAD" modified)) - (git-update-status-files (append added modified) 'uptodate)))) + (apply #'git-call-process-env nil nil "checkout" "HEAD" modified)) + (git-update-status-files (append added modified) 'uptodate) + (git-success-message "Reverted" files)))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." (interactive) (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) (when files - (apply #'git-run-command nil nil "update-index" "--" files) - (git-update-status-files files 'uptodate)))) + (apply #'git-call-process-env nil nil "update-index" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Resolved" files)))) (defun git-remove-handled () "Remove handled files from the status list." @@ -992,9 +998,11 @@ Return the list of files that haven't been handled." (interactive) (if (setq git-show-ignored (not git-show-ignored)) (progn + (message "Inserting ignored files...") (git-run-ls-files-with-excludes git-status nil 'ignored "-o" "-i") (git-refresh-files) - (git-refresh-ewoc-hf git-status)) + (git-refresh-ewoc-hf git-status) + (message "Inserting ignored files...done")) (git-remove-handled))) (defun git-toggle-show-unknown () @@ -1002,9 +1010,11 @@ Return the list of files that haven't been handled." (interactive) (if (setq git-show-unknown (not git-show-unknown)) (progn + (message "Inserting unknown files...") (git-run-ls-files-with-excludes git-status nil 'unknown "-o") (git-refresh-files) - (git-refresh-ewoc-hf git-status)) + (git-refresh-ewoc-hf git-status) + (message "Inserting unknown files...done")) (git-remove-handled))) (defun git-setup-diff-buffer (buffer) @@ -1207,7 +1217,8 @@ Return the list of files that haven't been handled." (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info))))) (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) (unless status (error "Not in git-status buffer.")) - (git-run-command nil nil "update-index" "--refresh") + (message "Refreshing git status...") + (git-call-process-env nil nil "update-index" "--refresh") (git-clear-status status) (git-update-status-files nil) ; restore file marks @@ -1219,6 +1230,7 @@ Return the list of files that haven't been handled." marked-files) (git-refresh-files)) ; move point to the current file name if any + (message "Refreshing git status...done") (let ((node (and cur-name (git-find-status-file status cur-name)))) (when node (ewoc-goto-node status node))))) -- cgit v1.2.3 From 0365d885ad17031de27440fec3553675d02aa4c3 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 29 Sep 2007 11:59:07 +0200 Subject: git.el: Update a file status in the git buffer upon save. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index ec2e699061..c2a1c3d1a2 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -36,7 +36,6 @@ ;; TODO ;; - portability to XEmacs ;; - better handling of subprocess errors -;; - hook into file save (after-save-hook) ;; - diff against other branch ;; - renaming files from the status buffer ;; - creating tags @@ -1352,9 +1351,24 @@ Commands: (cd dir) (git-status-mode) (git-refresh-status) - (goto-char (point-min))) + (goto-char (point-min)) + (add-hook 'after-save-hook 'git-update-saved-file)) (message "%s is not a git working tree." dir))) +(defun git-update-saved-file () + "Update the corresponding git-status buffer when a file is saved. +Meant to be used in `after-save-hook'." + (let* ((file (expand-file-name buffer-file-name)) + (dir (condition-case nil (git-get-top-dir (file-name-directory file)))) + (buffer (and dir (git-find-status-buffer dir)))) + (when buffer + (with-current-buffer buffer + (let ((filename (file-relative-name file dir))) + ; skip files located inside the .git directory + (unless (string-match "^\\.git/" filename) + (git-call-process-env nil nil "add" "--refresh" "--" filename) + (git-update-status-files (list filename) 'uptodate))))))) + (defun git-help () "Display help for Git mode." (interactive) -- cgit v1.2.3 From 72dc52bfe6c56e37f290dd2c428d82686d6647df Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 29 Sep 2007 11:59:32 +0200 Subject: git.el: Reset the permission flags when changing a file state. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index c2a1c3d1a2..4286d160a0 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -484,14 +484,15 @@ and returns the process output as a string." "Remove everything from the status list." (ewoc-filter status (lambda (info) nil))) -(defun git-set-files-state (files state) - "Set the state of a list of files." - (dolist (info files) - (unless (eq (git-fileinfo->state info) state) - (setf (git-fileinfo->state info) state) - (setf (git-fileinfo->rename-state info) nil) - (setf (git-fileinfo->orig-name info) nil) - (setf (git-fileinfo->needs-refresh info) t)))) +(defun git-set-fileinfo-state (info state) + "Set the state of a file info." + (unless (eq (git-fileinfo->state info) state) + (setf (git-fileinfo->state info) state + (git-fileinfo->old-perm info) 0 + (git-fileinfo->new-perm info) 0 + (git-fileinfo->rename-state info) nil + (git-fileinfo->orig-name info) nil + (git-fileinfo->needs-refresh info) t))) (defun git-status-filenames-map (status func files &rest args) "Apply FUNC to the status files names in the FILES list." @@ -510,14 +511,7 @@ and returns the process output as a string." (defun git-set-filenames-state (status files state) "Set the state of a list of named files." (when files - (git-status-filenames-map status - (lambda (info state) - (unless (eq (git-fileinfo->state info) state) - (setf (git-fileinfo->state info) state) - (setf (git-fileinfo->rename-state info) nil) - (setf (git-fileinfo->orig-name info) nil) - (setf (git-fileinfo->needs-refresh info) t))) - files state) + (git-status-filenames-map status #'git-set-fileinfo-state files state) (unless state ;; delete files whose state has been set to nil (ewoc-filter status (lambda (info) (git-fileinfo->state info)))))) @@ -800,7 +794,7 @@ Return the list of files that haven't been handled." (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)) - (git-set-files-state files 'uptodate) + (dolist (info files) (git-set-fileinfo-state info 'uptodate)) (git-call-process-env nil nil "rerere") (git-refresh-files) (git-refresh-ewoc-hf git-status) -- cgit v1.2.3 From e6dc8d60fbd2c84900a26545c5d360b0e202d95b Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Fri, 28 Sep 2007 15:24:26 +0100 Subject: post-receive-hook: Remove the From field from the generated email header so that the pusher's name is used Using the name of the committer of the revision at the tip of the updated ref is not sensible. That information is available in the email itself should it be wanted, and by supplying a "From", we were effectively hiding the person who performed the push - which is useful information in itself. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 1 - 1 file changed, 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 1f88099df4..cbbd02fadd 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -177,7 +177,6 @@ generate_email_header() # --- Email (all stdout will be the email) # Generate header cat <<-EOF - From: $committer To: $recipients Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe X-Git-Refname: $refname -- cgit v1.2.3 From fdfeb87c14e36d4c49b4481d01cd8bc103ba95f1 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 11 Oct 2007 17:49:21 -0400 Subject: fix contrib/hooks/post-receive-email hooks.recipients error message Have the error message for missing recipients actually report the missing config variable and not a fictional one. Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- contrib/hooks/post-receive-email | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index cbbd02fadd..b188aa3d67 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -138,7 +138,15 @@ generate_email() # Check if we've got anyone to send to if [ -z "$recipients" ]; then - echo >&2 "*** hooks.recipients is not set so no email will be sent" + case "$refname_type" in + "annotated tag") + config_name="hooks.announcelist" + ;; + *) + config_name="hooks.mailinglist" + ;; + esac + echo >&2 "*** $config_name is not set so no email will be sent" echo >&2 "*** for $refname update $oldrev->$newrev" exit 0 fi -- cgit v1.2.3 From 24ccd8b88ee578d8ea1d2a9a7be9ec4cf225fe73 Mon Sep 17 00:00:00 2001 From: Frederick Akalin Date: Fri, 5 Oct 2007 00:20:49 -0700 Subject: gtksourceview2 support for gitview Added support for gtksourceview2 module (pygtksourceview 1.90.x) in gitview. Also refactored code that creates the source buffer and view. Signed-off-by: Frederick Akalin Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- contrib/gitview/gitview | 53 +++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 22 deletions(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 5931766620..449ee69bf4 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -27,12 +27,20 @@ import math import string import fcntl +try: + import gtksourceview2 + have_gtksourceview2 = True +except ImportError: + have_gtksourceview2 = False + try: import gtksourceview have_gtksourceview = True except ImportError: have_gtksourceview = False - print "Running without gtksourceview module" + +if not have_gtksourceview2 and not have_gtksourceview: + print "Running without gtksourceview2 or gtksourceview module" re_ident = re.compile('(author|committer) (?P.*) (?P\d+) (?P[+-]\d{4})') @@ -58,6 +66,26 @@ def show_date(epoch, tz): return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(secs)) +def get_source_buffer_and_view(): + if have_gtksourceview2: + buffer = gtksourceview2.Buffer() + slm = gtksourceview2.LanguageManager() + gsl = slm.get_language("diff") + buffer.set_highlight_syntax(True) + buffer.set_language(gsl) + view = gtksourceview2.View(buffer) + elif have_gtksourceview: + buffer = gtksourceview.SourceBuffer() + slm = gtksourceview.SourceLanguagesManager() + gsl = slm.get_language_from_mime_type("text/x-patch") + buffer.set_highlight(True) + buffer.set_language(gsl) + view = gtksourceview.SourceView(buffer) + else: + buffer = gtk.TextBuffer() + view = gtk.TextView(buffer) + return (buffer, view) + class CellRendererGraph(gtk.GenericCellRenderer): """Cell renderer for directed graph. @@ -582,17 +610,7 @@ class DiffWindow(object): hpan.pack1(scrollwin, True, True) scrollwin.show() - if have_gtksourceview: - self.buffer = gtksourceview.SourceBuffer() - slm = gtksourceview.SourceLanguagesManager() - gsl = slm.get_language_from_mime_type("text/x-patch") - self.buffer.set_highlight(True) - self.buffer.set_language(gsl) - sourceview = gtksourceview.SourceView(self.buffer) - else: - self.buffer = gtk.TextBuffer() - sourceview = gtk.TextView(self.buffer) - + (self.buffer, sourceview) = get_source_buffer_and_view() sourceview.set_editable(False) sourceview.modify_font(pango.FontDescription("Monospace")) @@ -956,16 +974,7 @@ class GitView(object): vbox.pack_start(scrollwin, expand=True, fill=True) scrollwin.show() - if have_gtksourceview: - self.message_buffer = gtksourceview.SourceBuffer() - slm = gtksourceview.SourceLanguagesManager() - gsl = slm.get_language_from_mime_type("text/x-patch") - self.message_buffer.set_highlight(True) - self.message_buffer.set_language(gsl) - sourceview = gtksourceview.SourceView(self.message_buffer) - else: - self.message_buffer = gtk.TextBuffer() - sourceview = gtk.TextView(self.message_buffer) + (self.message_buffer, sourceview) = get_source_buffer_and_view() sourceview.set_editable(False) sourceview.modify_font(pango.FontDescription("Monospace")) -- cgit v1.2.3 From 7c0d741a3e8db662419cc841e3068b2a8880a109 Mon Sep 17 00:00:00 2001 From: Michael Gebetsroither Date: Sat, 6 Oct 2007 23:16:51 +0200 Subject: hg-to-git speedup through selectable repack intervals Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- contrib/hg-to-git/hg-to-git.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 37337ff01f..7a1c3e497f 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -29,6 +29,8 @@ hgvers = {} hgchildren = {} # Current branch for each hg revision hgbranch = {} +# Number of new changesets converted from hg +hgnewcsets = 0 #------------------------------------------------------------------------------ @@ -40,6 +42,8 @@ def usage(): options: -s, --gitstate=FILE: name of the state to be saved/read for incrementals + -n, --nrepack=INT: number of changesets that will trigger + a repack (default=0, -1 to deactivate) required: hgprj: name of the HG project to import (directory) @@ -68,14 +72,16 @@ def getgitenv(user, date): #------------------------------------------------------------------------------ state = '' +opt_nrepack = 0 try: - opts, args = getopt.getopt(sys.argv[1:], 's:t:', ['gitstate=', 'tempdir=']) + opts, args = getopt.getopt(sys.argv[1:], 's:t:n:', ['gitstate=', 'tempdir=', 'nrepack=']) for o, a in opts: if o in ('-s', '--gitstate'): state = a state = os.path.abspath(state) - + if o in ('-n', '--nrepack'): + opt_nrepack = int(a) if len(args) != 1: raise('params') except: @@ -138,6 +144,7 @@ for cset in range(int(tip) + 1): # incremental, already seen if hgvers.has_key(str(cset)): continue + hgnewcsets += 1 # get info prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines() @@ -222,7 +229,8 @@ for cset in range(int(tip) + 1): print 'record', cset, '->', vvv hgvers[str(cset)] = vvv -os.system('git-repack -a -d') +if hgnewcsets >= opt_nrepack and opt_nrepack != -1: + os.system('git-repack -a -d') # write the state for incrementals if state: -- cgit v1.2.3 From a9834f58335b81f48e137beb33afb5bb799cdae8 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Tue, 9 Oct 2007 16:16:09 +0200 Subject: Add 'git-p4 commit' as an alias for 'git-p4 submit' Given that git uses 'commit', git-p4's 'sumbit' was a bit confusing at times; often making me do 'git submit' and 'git-p4 commit' instead. Signed-off-by: Marius Storm-Olsen Acked-By: Simon Hausmann Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- contrib/fast-import/git-p4 | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 557649a14a..52cd2a46ba 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1651,6 +1651,7 @@ def printUsage(commands): commands = { "debug" : P4Debug, "submit" : P4Submit, + "commit" : P4Submit, "sync" : P4Sync, "rebase" : P4Rebase, "clone" : P4Clone, -- cgit v1.2.3 From 03618b9df84a0e94e36fdb27060e605e85b956e9 Mon Sep 17 00:00:00 2001 From: Josh England Date: Tue, 9 Oct 2007 10:04:42 -0600 Subject: Minor usage update in setgitperms.perl Signed-off-by: Josh England Signed-off-by: Lars Hjemli Signed-off-by: Shawn O. Pearce --- contrib/hooks/setgitperms.perl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/setgitperms.perl b/contrib/hooks/setgitperms.perl index 5e3b89def2..dab7c8e3a1 100644 --- a/contrib/hooks/setgitperms.perl +++ b/contrib/hooks/setgitperms.perl @@ -8,13 +8,14 @@ # To save permissions/ownership data, place this script in your .git/hooks # directory and enable a `pre-commit` hook with the following lines: # #!/bin/sh -# . git-sh-setup +# SUBDIRECTORY_OK=1 . git-sh-setup # $GIT_DIR/hooks/setgitperms.perl -r # # To restore permissions/ownership data, place this script in your .git/hooks -# directory and enable a `post-merge` hook with the following lines: +# directory and enable a `post-merge` and `post-checkout` hook with the +# following lines: # #!/bin/sh -# . git-sh-setup +# SUBDIRECTORY_OK=1 . git-sh-setup # $GIT_DIR/hooks/setgitperms.perl -w # use strict; -- cgit v1.2.3 From a2d6b872dbf4e65525c9ba55e820e2ea26011ce1 Mon Sep 17 00:00:00 2001 From: Robert Schiele Date: Thu, 18 Oct 2007 00:27:51 +0200 Subject: fixing output of non-fast-forward output of post-receive-email post-receive-email has one place where the variable fast_forward is not spelled correctly. At the same place the logic was reversed. The combination of both bugs made the script work correctly for fast-forward commits but not for non-fast-forward ones. This change fixes this to be correct in both cases. Signed-off-by: Robert Schiele Signed-off-by: Shawn O. Pearce --- contrib/hooks/post-receive-email | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index cbbd02fadd..28a06c7f38 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -323,7 +323,7 @@ generate_update_branch_email() echo " via $rev ($revtype)" done - if [ -z "$fastforward" ]; then + if [ "$fast_forward" ]; then echo " from $oldrev ($oldrev_type)" else # 1. Existing revisions were removed. In this case newrev is a -- cgit v1.2.3 From 209471493afbf30d5c1fc80feadfc4836b86dc42 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 13 Sep 2007 22:10:18 +0200 Subject: git-p4: When skipping a patch as part of "git-p4 submit" make sure we correctly revert to the previous state of the files using "p4 revert". Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 52cd2a46ba..e1dc263013 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -529,6 +529,10 @@ class P4Submit(Command): "and with .rej files / [w]rite the patch to a file (patch.txt) ") if response == "s": print "Skipping! Good luck with the next patches..." + for f in editedFiles: + system("p4 revert \"%s\"" % f); + for f in filesToAdd: + system("rm %s" %f) return elif response == "a": os.system(applyPatchCmd) -- cgit v1.2.3 From d9a5f25b67951eb68e80d872611542b58ce5aa33 Mon Sep 17 00:00:00 2001 From: Chris Pettitt Date: Mon, 15 Oct 2007 22:15:06 -0700 Subject: git-p4 support for perforce renames. The current git-p4 implementation does support file renames. However, because it does not use the "p4 integrate" command, the history for the renamed file is not linked to the new file. This changeset adds support for perforce renames with the integrate command. Currently this feature is only enabled when calling git-p4 submit with the -M option. This is intended to look and behave similar to the "detect renames" feature of other git commands. The following sequence is used for renamed files: p4 integrate -Dt x x' p4 edit x' rm x' git apply p4 delete x By default, perforce will not allow an integration with a target file that has been deleted. That is, if x' in the example above is the name of a previously deleted file then perforce will fail the integrate. The -Dt option tells perforce to allow the target of integrate to be a previously deleted file. Signed-off-by: Chris Pettitt Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e1dc263013..bf33f74b70 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -399,6 +399,7 @@ class P4Submit(Command): optparse.make_option("--dry-run", action="store_true"), optparse.make_option("--direct", dest="directSubmit", action="store_true"), optparse.make_option("--trust-me-like-a-fool", dest="trustMeLikeAFool", action="store_true"), + optparse.make_option("-M", dest="detectRename", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" @@ -411,6 +412,7 @@ class P4Submit(Command): self.origin = "" self.directSubmit = False self.trustMeLikeAFool = False + self.detectRename = False self.verbose = False self.isWindows = (platform.system() == "Windows") @@ -491,7 +493,8 @@ class P4Submit(Command): diff = self.diffStatus else: print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) - diff = read_pipe_lines("git diff-tree -r --name-status \"%s^\" \"%s\"" % (id, id)) + diffOpts = ("", "-M")[self.detectRename] + diff = read_pipe_lines("git diff-tree -r --name-status %s \"%s^\" \"%s\"" % (diffOpts, id, id)) filesToAdd = set() filesToDelete = set() editedFiles = set() @@ -509,6 +512,13 @@ class P4Submit(Command): filesToDelete.add(path) if path in filesToAdd: filesToAdd.remove(path) + elif modifier == "R": + src, dest = line.strip().split("\t")[1:3] + system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) + system("p4 edit \"%s\"" % (dest)) + os.unlink(dest) + editedFiles.add(dest) + filesToDelete.add(src) else: die("unknown modifier %s for %s" % (modifier, path)) -- cgit v1.2.3 From 3697c5f37a8b7b15d0a3be51d05147654a951115 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 28 Oct 2007 11:05:11 +0100 Subject: git.el: Fix typo in "Reverted file" message. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 4286d160a0..8cfbdd7be4 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -955,7 +955,7 @@ Return the list of files that haven't been handled." (when modified (apply #'git-call-process-env nil nil "checkout" "HEAD" modified)) (git-update-status-files (append added modified) 'uptodate) - (git-success-message "Reverted" files)))) + (git-success-message "Reverted" (git-get-filenames files))))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." -- cgit v1.2.3 From 6df023884b87ef140829d78b67fab90a7f9b1211 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 28 Oct 2007 11:05:45 +0100 Subject: git.el: Fix typo in git-update-saved-file error handling. Spotted by Matthieu Lemerre. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 8cfbdd7be4..0e5091c1b7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1353,7 +1353,7 @@ Commands: "Update the corresponding git-status buffer when a file is saved. Meant to be used in `after-save-hook'." (let* ((file (expand-file-name buffer-file-name)) - (dir (condition-case nil (git-get-top-dir (file-name-directory file)))) + (dir (condition-case nil (git-get-top-dir (file-name-directory file)) (error nil))) (buffer (and dir (git-find-status-buffer dir)))) (when buffer (with-current-buffer buffer -- cgit v1.2.3 From 2f6e86a86fb9830c0c3205a317f6205f156cfacc Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 28 Oct 2007 11:06:27 +0100 Subject: git.el: Refresh only the changed file marks when marking/unmarking all. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 0e5091c1b7..e5ee8ce58b 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -842,7 +842,8 @@ Return the list of files that haven't been handled." "Mark all files." (interactive) (unless git-status (error "Not in git-status buffer.")) - (ewoc-map (lambda (info) (setf (git-fileinfo->marked info) t) t) git-status) + (ewoc-map (lambda (info) (unless (git-fileinfo->marked info) + (setf (git-fileinfo->marked info) t))) git-status) ; move back to goal column after invalidate (when goal-column (move-to-column goal-column))) @@ -850,7 +851,9 @@ Return the list of files that haven't been handled." "Unmark all files." (interactive) (unless git-status (error "Not in git-status buffer.")) - (ewoc-map (lambda (info) (setf (git-fileinfo->marked info) nil) t) git-status) + (ewoc-map (lambda (info) (when (git-fileinfo->marked info) + (setf (git-fileinfo->marked info) nil) + t)) git-status) ; move back to goal column after invalidate (when goal-column (move-to-column goal-column))) -- cgit v1.2.3 From d53a35020d380c199b010c9884ab15995f8e982b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 28 Oct 2007 11:07:14 +0100 Subject: git.el: Run git-gc --auto after commits. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index e5ee8ce58b..e147da0596 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -796,6 +796,7 @@ Return the list of files that haven't been handled." (with-current-buffer buffer (erase-buffer)) (dolist (info files) (git-set-fileinfo-state info 'uptodate)) (git-call-process-env nil nil "rerere") + (git-call-process-env nil nil "gc" "--auto") (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) -- cgit v1.2.3 From fee9832a8dea5d9c98c5c3a4797615d52814df16 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 30 Oct 2007 14:24:27 +0000 Subject: No longer install git-svnimport, move to contrib/examples This has been proposed for a few times without much reaction from the list. Actually remove it to see who screams. Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 - contrib/examples/git-svnimport.perl | 976 +++++++++++++++++++++++++++++++++ contrib/examples/git-svnimport.txt | 179 ++++++ 3 files changed, 1155 insertions(+), 1 deletion(-) create mode 100755 contrib/examples/git-svnimport.perl create mode 100644 contrib/examples/git-svnimport.txt (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e760930740..599b2fc571 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -346,7 +346,6 @@ __git_commands () ssh-*) : transport;; stripspace) : plumbing;; svn) : import export;; - svnimport) : import;; symbolic-ref) : plumbing;; tar-tree) : deprecated;; unpack-file) : plumbing;; diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl new file mode 100755 index 0000000000..ea8c1b2f60 --- /dev/null +++ b/contrib/examples/git-svnimport.perl @@ -0,0 +1,976 @@ +#!/usr/bin/perl -w + +# This tool is copyright (c) 2005, Matthias Urlichs. +# It is released under the Gnu Public License, version 2. +# +# The basic idea is to pull and analyze SVN changes. +# +# Checking out the files is done by a single long-running SVN connection. +# +# The head revision is on branch "origin" by default. +# You can change that with the '-o' option. + +use strict; +use warnings; +use Getopt::Std; +use File::Copy; +use File::Spec; +use File::Temp qw(tempfile); +use File::Path qw(mkpath); +use File::Basename qw(basename dirname); +use Time::Local; +use IO::Pipe; +use POSIX qw(strftime dup2); +use IPC::Open2; +use SVN::Core; +use SVN::Ra; + +die "Need SVN:Core 1.2.1 or better" if $SVN::Core::VERSION lt "1.2.1"; + +$SIG{'PIPE'}="IGNORE"; +$ENV{'TZ'}="UTC"; + +our($opt_h,$opt_o,$opt_v,$opt_u,$opt_C,$opt_i,$opt_m,$opt_M,$opt_t,$opt_T, + $opt_b,$opt_r,$opt_I,$opt_A,$opt_s,$opt_l,$opt_d,$opt_D,$opt_S,$opt_F, + $opt_P,$opt_R); + +sub usage() { + print STDERR <new_default; + +@ARGV == 1 or @ARGV == 2 or usage(); + +$opt_o ||= "origin"; +$opt_s ||= 1; +my $git_tree = $opt_C; +$git_tree ||= "."; + +my $svn_url = $ARGV[0]; +my $svn_dir = $ARGV[1]; + +our @mergerx = (); +if ($opt_m) { + my $branch_esc = quotemeta ($branch_name); + my $trunk_esc = quotemeta ($trunk_name); + @mergerx = + ( + qr!\b(?:merg(?:ed?|ing))\b.*?\b((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i, + qr!\b(?:from|of)\W+((?:(?<=$branch_esc/)[\w\.\-]+)|(?:$trunk_esc))\b!i, + qr!\b(?:from|of)\W+(?:the )?([\w\.\-]+)[-\s]branch\b!i + ); +} +if ($opt_M) { + unshift (@mergerx, qr/$opt_M/); +} + +# Absolutize filename now, since we will have chdir'ed by the time we +# get around to opening it. +$opt_A = File::Spec->rel2abs($opt_A) if $opt_A; + +our %users = (); +our $users_file = undef; +sub read_users($) { + $users_file = File::Spec->rel2abs(@_); + die "Cannot open $users_file\n" unless -f $users_file; + open(my $authors,$users_file); + while(<$authors>) { + chomp; + next unless /^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/; + (my $user,my $name,my $email) = ($1,$2,$3); + $users{$user} = [$name,$email]; + } + close($authors); +} + +select(STDERR); $|=1; select(STDOUT); + + +package SVNconn; +# Basic SVN connection. +# We're only interested in connecting and downloading, so ... + +use File::Spec; +use File::Temp qw(tempfile); +use POSIX qw(strftime dup2); +use Fcntl qw(SEEK_SET); + +sub new { + my($what,$repo) = @_; + $what=ref($what) if ref($what); + + my $self = {}; + $self->{'buffer'} = ""; + bless($self,$what); + + $repo =~ s#/+$##; + $self->{'fullrep'} = $repo; + $self->conn(); + + return $self; +} + +sub conn { + my $self = shift; + my $repo = $self->{'fullrep'}; + my $auth = SVN::Core::auth_open ([SVN::Client::get_simple_provider, + SVN::Client::get_ssl_server_trust_file_provider, + SVN::Client::get_username_provider]); + my $s = SVN::Ra->new(url => $repo, auth => $auth, pool => $root_pool); + die "SVN connection to $repo: $!\n" unless defined $s; + $self->{'svn'} = $s; + $self->{'repo'} = $repo; + $self->{'maxrev'} = $s->get_latest_revnum(); +} + +sub file { + my($self,$path,$rev) = @_; + + my ($fh, $name) = tempfile('gitsvn.XXXXXX', + DIR => File::Spec->tmpdir(), UNLINK => 1); + + print "... $rev $path ...\n" if $opt_v; + my (undef, $properties); + $path =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; + eval { (undef, $properties) + = $self->{'svn'}->get_file($path,$rev,$fh); }; + if($@) { + return undef if $@ =~ /Attempted to get checksum/; + die $@; + } + my $mode; + if (exists $properties->{'svn:executable'}) { + $mode = '100755'; + } elsif (exists $properties->{'svn:special'}) { + my ($special_content, $filesize); + $filesize = tell $fh; + seek $fh, 0, SEEK_SET; + read $fh, $special_content, $filesize; + if ($special_content =~ s/^link //) { + $mode = '120000'; + seek $fh, 0, SEEK_SET; + truncate $fh, 0; + print $fh $special_content; + } else { + die "unexpected svn:special file encountered"; + } + } else { + $mode = '100644'; + } + close ($fh); + + return ($name, $mode); +} + +sub ignore { + my($self,$path,$rev) = @_; + + print "... $rev $path ...\n" if $opt_v; + $path =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; + my (undef,undef,$properties) + = $self->{'svn'}->get_dir($path,$rev,undef); + if (exists $properties->{'svn:ignore'}) { + my ($fh, $name) = tempfile('gitsvn.XXXXXX', + DIR => File::Spec->tmpdir(), + UNLINK => 1); + print $fh $properties->{'svn:ignore'}; + close($fh); + return $name; + } else { + return undef; + } +} + +sub dir_list { + my($self,$path,$rev) = @_; + $path =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; + my ($dirents,undef,$properties) + = $self->{'svn'}->get_dir($path,$rev,undef); + return $dirents; +} + +package main; +use URI; + +our $svn = $svn_url; +$svn .= "/$svn_dir" if defined $svn_dir; +my $svn2 = SVNconn->new($svn); +$svn = SVNconn->new($svn); + +my $lwp_ua; +if($opt_d or $opt_D) { + $svn_url = URI->new($svn_url)->canonical; + if($opt_D) { + $svn_dir =~ s#/*$#/#; + } else { + $svn_dir = ""; + } + if ($svn_url->scheme eq "http") { + use LWP::UserAgent; + $lwp_ua = LWP::UserAgent->new(keep_alive => 1, requests_redirectable => []); + } else { + print STDERR "Warning: not HTTP; turning off direct file access\n"; + $opt_d=0; + } +} + +sub pdate($) { + my($d) = @_; + $d =~ m#(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)# + or die "Unparseable date: $d\n"; + my $y=$1; $y-=1900 if $y>1900; + return timegm($6||0,$5,$4,$3,$2-1,$y); +} + +sub getwd() { + my $pwd = `pwd`; + chomp $pwd; + return $pwd; +} + + +sub get_headref($$) { + my $name = shift; + my $git_dir = shift; + my $sha; + + if (open(C,"$git_dir/refs/heads/$name")) { + chomp($sha = ); + close(C); + length($sha) == 40 + or die "Cannot get head id for $name ($sha): $!\n"; + } + return $sha; +} + + +-d $git_tree + or mkdir($git_tree,0777) + or die "Could not create $git_tree: $!"; +chdir($git_tree); + +my $orig_branch = ""; +my $forward_master = 0; +my %branches; + +my $git_dir = $ENV{"GIT_DIR"} || ".git"; +$git_dir = getwd()."/".$git_dir unless $git_dir =~ m#^/#; +$ENV{"GIT_DIR"} = $git_dir; +my $orig_git_index; +$orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE}; +my ($git_ih, $git_index) = tempfile('gitXXXXXX', SUFFIX => '.idx', + DIR => File::Spec->tmpdir()); +close ($git_ih); +$ENV{GIT_INDEX_FILE} = $git_index; +my $maxnum = 0; +my $last_rev = ""; +my $last_branch; +my $current_rev = $opt_s || 1; +unless(-d $git_dir) { + system("git-init"); + die "Cannot init the GIT db at $git_tree: $?\n" if $?; + system("git-read-tree"); + die "Cannot init an empty tree: $?\n" if $?; + + $last_branch = $opt_o; + $orig_branch = ""; +} else { + -f "$git_dir/refs/heads/$opt_o" + or die "Branch '$opt_o' does not exist.\n". + "Either use the correct '-o branch' option,\n". + "or import to a new repository.\n"; + + -f "$git_dir/svn2git" + or die "'$git_dir/svn2git' does not exist.\n". + "You need that file for incremental imports.\n"; + open(F, "git-symbolic-ref HEAD |") or + die "Cannot run git-symbolic-ref: $!\n"; + chomp ($last_branch = ); + $last_branch = basename($last_branch); + close(F); + unless($last_branch) { + warn "Cannot read the last branch name: $! -- assuming 'master'\n"; + $last_branch = "master"; + } + $orig_branch = $last_branch; + $last_rev = get_headref($orig_branch, $git_dir); + if (-f "$git_dir/SVN2GIT_HEAD") { + die <) { + chomp; + my($num,$branch,$ref) = split; + $branches{$branch}{$num} = $ref; + $branches{$branch}{"LAST"} = $ref; + $current_rev = $num+1 if $current_rev <= $num; + } + close($B); +} +-d $git_dir + or die "Could not create git subdir ($git_dir).\n"; + +my $default_authors = "$git_dir/svn-authors"; +if ($opt_A) { + read_users($opt_A); + copy($opt_A,$default_authors) or die "Copy failed: $!"; +} else { + read_users($default_authors) if -f $default_authors; +} + +open BRANCHES,">>", "$git_dir/svn2git"; + +sub node_kind($$) { + my ($svnpath, $revision) = @_; + $svnpath =~ s#^/*##; + my $subpool = SVN::Pool::new_default_sub; + my $kind = $svn->{'svn'}->check_path($svnpath,$revision); + return $kind; +} + +sub get_file($$$) { + my($svnpath,$rev,$path) = @_; + + # now get it + my ($name,$mode); + if($opt_d) { + my($req,$res); + + # /svn/!svn/bc/2/django/trunk/django-docs/build.py + my $url=$svn_url->clone(); + $url->path($url->path."/!svn/bc/$rev/$svn_dir$svnpath"); + print "... $path...\n" if $opt_v; + $req = HTTP::Request->new(GET => $url); + $res = $lwp_ua->request($req); + if ($res->is_success) { + my $fh; + ($fh, $name) = tempfile('gitsvn.XXXXXX', + DIR => File::Spec->tmpdir(), UNLINK => 1); + print $fh $res->content; + close($fh) or die "Could not write $name: $!\n"; + } else { + return undef if $res->code == 301; # directory? + die $res->status_line." at $url\n"; + } + $mode = '0644'; # can't obtain mode via direct http request? + } else { + ($name,$mode) = $svn->file("$svnpath",$rev); + return undef unless defined $name; + } + + my $pid = open(my $F, '-|'); + die $! unless defined $pid; + if (!$pid) { + exec("git-hash-object", "-w", $name) + or die "Cannot create object: $!\n"; + } + my $sha = <$F>; + chomp $sha; + close $F; + unlink $name; + return [$mode, $sha, $path]; +} + +sub get_ignore($$$$$) { + my($new,$old,$rev,$path,$svnpath) = @_; + + return unless $opt_I; + my $name = $svn->ignore("$svnpath",$rev); + if ($path eq '/') { + $path = $opt_I; + } else { + $path = File::Spec->catfile($path,$opt_I); + } + if (defined $name) { + my $pid = open(my $F, '-|'); + die $! unless defined $pid; + if (!$pid) { + exec("git-hash-object", "-w", $name) + or die "Cannot create object: $!\n"; + } + my $sha = <$F>; + chomp $sha; + close $F; + unlink $name; + push(@$new,['0644',$sha,$path]); + } elsif (defined $old) { + push(@$old,$path); + } +} + +sub project_path($$) +{ + my ($path, $project) = @_; + + $path = "/".$path unless ($path =~ m#^\/#) ; + return $1 if ($path =~ m#^$project\/(.*)$#); + + $path =~ s#\.#\\\.#g; + $path =~ s#\+#\\\+#g; + return "/" if ($project =~ m#^$path.*$#); + + return undef; +} + +sub split_path($$) { + my($rev,$path) = @_; + my $branch; + + if($path =~ s#^/\Q$tag_name\E/([^/]+)/?##) { + $branch = "/$1"; + } elsif($path =~ s#^/\Q$trunk_name\E/?##) { + $branch = "/"; + } elsif($path =~ s#^/\Q$branch_name\E/([^/]+)/?##) { + $branch = $1; + } else { + my %no_error = ( + "/" => 1, + "/$tag_name" => 1, + "/$branch_name" => 1 + ); + print STDERR "$rev: Unrecognized path: $path\n" unless (defined $no_error{$path}); + return () + } + if ($path eq "") { + $path = "/"; + } elsif ($project_name) { + $path = project_path($path, $project_name); + } + return ($branch,$path); +} + +sub branch_rev($$) { + + my ($srcbranch,$uptorev) = @_; + + my $bbranches = $branches{$srcbranch}; + my @revs = reverse sort { ($a eq 'LAST' ? 0 : $a) <=> ($b eq 'LAST' ? 0 : $b) } keys %$bbranches; + my $therev; + foreach my $arev(@revs) { + next if ($arev eq 'LAST'); + if ($arev <= $uptorev) { + $therev = $arev; + last; + } + } + return $therev; +} + +sub expand_svndir($$$); + +sub expand_svndir($$$) +{ + my ($svnpath, $rev, $path) = @_; + my @list; + get_ignore(\@list, undef, $rev, $path, $svnpath); + my $dirents = $svn->dir_list($svnpath, $rev); + foreach my $p(keys %$dirents) { + my $kind = node_kind($svnpath.'/'.$p, $rev); + if ($kind eq $SVN::Node::file) { + my $f = get_file($svnpath.'/'.$p, $rev, $path.'/'.$p); + push(@list, $f) if $f; + } elsif ($kind eq $SVN::Node::dir) { + push(@list, + expand_svndir($svnpath.'/'.$p, $rev, $path.'/'.$p)); + } + } + return @list; +} + +sub copy_path($$$$$$$$) { + # Somebody copied a whole subdirectory. + # We need to find the index entries from the old version which the + # SVN log entry points to, and add them to the new place. + + my($newrev,$newbranch,$path,$oldpath,$rev,$node_kind,$new,$parents) = @_; + + my($srcbranch,$srcpath) = split_path($rev,$oldpath); + unless(defined $srcbranch && defined $srcpath) { + print "Path not found when copying from $oldpath @ $rev.\n". + "Will try to copy from original SVN location...\n" + if $opt_v; + push (@$new, expand_svndir($oldpath, $rev, $path)); + return; + } + my $therev = branch_rev($srcbranch, $rev); + my $gitrev = $branches{$srcbranch}{$therev}; + unless($gitrev) { + print STDERR "$newrev:$newbranch: could not find $oldpath \@ $rev\n"; + return; + } + if ($srcbranch ne $newbranch) { + push(@$parents, $branches{$srcbranch}{'LAST'}); + } + print "$newrev:$newbranch:$path: copying from $srcbranch:$srcpath @ $rev\n" if $opt_v; + if ($node_kind eq $SVN::Node::dir) { + $srcpath =~ s#/*$#/#; + } + + my $pid = open my $f,'-|'; + die $! unless defined $pid; + if (!$pid) { + exec("git-ls-tree","-r","-z",$gitrev,$srcpath) + or die $!; + } + local $/ = "\0"; + while(<$f>) { + chomp; + my($m,$p) = split(/\t/,$_,2); + my($mode,$type,$sha1) = split(/ /,$m); + next if $type ne "blob"; + if ($node_kind eq $SVN::Node::dir) { + $p = $path . substr($p,length($srcpath)-1); + } else { + $p = $path; + } + push(@$new,[$mode,$sha1,$p]); + } + close($f) or + print STDERR "$newrev:$newbranch: could not list files in $oldpath \@ $rev\n"; +} + +sub commit { + my($branch, $changed_paths, $revision, $author, $date, $message) = @_; + my($committer_name,$committer_email,$dest); + my($author_name,$author_email); + my(@old,@new,@parents); + + if (not defined $author or $author eq "") { + $committer_name = $committer_email = "unknown"; + } elsif (defined $users_file) { + die "User $author is not listed in $users_file\n" + unless exists $users{$author}; + ($committer_name,$committer_email) = @{$users{$author}}; + } elsif ($author =~ /^(.*?)\s+<(.*)>$/) { + ($committer_name, $committer_email) = ($1, $2); + } else { + $author =~ s/^<(.*)>$/$1/; + $committer_name = $committer_email = $author; + } + + if ($opt_F && $message =~ /From:\s+(.*?)\s+<(.*)>\s*\n/) { + ($author_name, $author_email) = ($1, $2); + print "Author from From: $1 <$2>\n" if ($opt_v);; + } elsif ($opt_S && $message =~ /Signed-off-by:\s+(.*?)\s+<(.*)>\s*\n/) { + ($author_name, $author_email) = ($1, $2); + print "Author from Signed-off-by: $1 <$2>\n" if ($opt_v);; + } else { + $author_name = $committer_name; + $author_email = $committer_email; + } + + $date = pdate($date); + + my $tag; + my $parent; + if($branch eq "/") { # trunk + $parent = $opt_o; + } elsif($branch =~ m#^/(.+)#) { # tag + $tag = 1; + $parent = $1; + } else { # "normal" branch + # nothing to do + $parent = $branch; + } + $dest = $parent; + + my $prev = $changed_paths->{"/"}; + if($prev and $prev->[0] eq "A") { + delete $changed_paths->{"/"}; + my $oldpath = $prev->[1]; + my $rev; + if(defined $oldpath) { + my $p; + ($parent,$p) = split_path($revision,$oldpath); + if(defined $parent) { + if($parent eq "/") { + $parent = $opt_o; + } else { + $parent =~ s#^/##; # if it's a tag + } + } + } else { + $parent = undef; + } + } + + my $rev; + if($revision > $opt_s and defined $parent) { + open(H,'-|',"git-rev-parse","--verify",$parent); + $rev = ; + close(H) or do { + print STDERR "$revision: cannot find commit '$parent'!\n"; + return; + }; + chop $rev; + if(length($rev) != 40) { + print STDERR "$revision: cannot find commit '$parent'!\n"; + return; + } + $rev = $branches{($parent eq $opt_o) ? "/" : $parent}{"LAST"}; + if($revision != $opt_s and not $rev) { + print STDERR "$revision: do not know ancestor for '$parent'!\n"; + return; + } + } else { + $rev = undef; + } + +# if($prev and $prev->[0] eq "A") { +# if(not $tag) { +# unless(open(H,"> $git_dir/refs/heads/$branch")) { +# print STDERR "$revision: Could not create branch $branch: $!\n"; +# $state=11; +# next; +# } +# print H "$rev\n" +# or die "Could not write branch $branch: $!"; +# close(H) +# or die "Could not write branch $branch: $!"; +# } +# } + if(not defined $rev) { + unlink($git_index); + } elsif ($rev ne $last_rev) { + print "Switching from $last_rev to $rev ($branch)\n" if $opt_v; + system("git-read-tree", $rev); + die "read-tree failed for $rev: $?\n" if $?; + $last_rev = $rev; + } + + push (@parents, $rev) if defined $rev; + + my $cid; + if($tag and not %$changed_paths) { + $cid = $rev; + } else { + my @paths = sort keys %$changed_paths; + foreach my $path(@paths) { + my $action = $changed_paths->{$path}; + + if ($action->[0] eq "R") { + # refer to a file/tree in an earlier commit + push(@old,$path); # remove any old stuff + } + if(($action->[0] eq "A") || ($action->[0] eq "R")) { + my $node_kind = node_kind($action->[3], $revision); + if ($node_kind eq $SVN::Node::file) { + my $f = get_file($action->[3], + $revision, $path); + if ($f) { + push(@new,$f) if $f; + } else { + my $opath = $action->[3]; + print STDERR "$revision: $branch: could not fetch '$opath'\n"; + } + } elsif ($node_kind eq $SVN::Node::dir) { + if($action->[1]) { + copy_path($revision, $branch, + $path, $action->[1], + $action->[2], $node_kind, + \@new, \@parents); + } else { + get_ignore(\@new, \@old, $revision, + $path, $action->[3]); + } + } + } elsif ($action->[0] eq "D") { + push(@old,$path); + } elsif ($action->[0] eq "M") { + my $node_kind = node_kind($action->[3], $revision); + if ($node_kind eq $SVN::Node::file) { + my $f = get_file($action->[3], + $revision, $path); + push(@new,$f) if $f; + } elsif ($node_kind eq $SVN::Node::dir) { + get_ignore(\@new, \@old, $revision, + $path, $action->[3]); + } + } else { + die "$revision: unknown action '".$action->[0]."' for $path\n"; + } + } + + while(@old) { + my @o1; + if(@old > 55) { + @o1 = splice(@old,0,50); + } else { + @o1 = @old; + @old = (); + } + my $pid = open my $F, "-|"; + die "$!" unless defined $pid; + if (!$pid) { + exec("git-ls-files", "-z", @o1) or die $!; + } + @o1 = (); + local $/ = "\0"; + while(<$F>) { + chomp; + push(@o1,$_); + } + close($F); + + while(@o1) { + my @o2; + if(@o1 > 55) { + @o2 = splice(@o1,0,50); + } else { + @o2 = @o1; + @o1 = (); + } + system("git-update-index","--force-remove","--",@o2); + die "Cannot remove files: $?\n" if $?; + } + } + while(@new) { + my @n2; + if(@new > 12) { + @n2 = splice(@new,0,10); + } else { + @n2 = @new; + @new = (); + } + system("git-update-index","--add", + (map { ('--cacheinfo', @$_) } @n2)); + die "Cannot add files: $?\n" if $?; + } + + my $pid = open(C,"-|"); + die "Cannot fork: $!" unless defined $pid; + unless($pid) { + exec("git-write-tree"); + die "Cannot exec git-write-tree: $!\n"; + } + chomp(my $tree = ); + length($tree) == 40 + or die "Cannot get tree id ($tree): $!\n"; + close(C) + or die "Error running git-write-tree: $?\n"; + print "Tree ID $tree\n" if $opt_v; + + my $pr = IO::Pipe->new() or die "Cannot open pipe: $!\n"; + my $pw = IO::Pipe->new() or die "Cannot open pipe: $!\n"; + $pid = fork(); + die "Fork: $!\n" unless defined $pid; + unless($pid) { + $pr->writer(); + $pw->reader(); + open(OUT,">&STDOUT"); + dup2($pw->fileno(),0); + dup2($pr->fileno(),1); + $pr->close(); + $pw->close(); + + my @par = (); + + # loose detection of merges + # based on the commit msg + foreach my $rx (@mergerx) { + if ($message =~ $rx) { + my $mparent = $1; + if ($mparent eq 'HEAD') { $mparent = $opt_o }; + if ( -e "$git_dir/refs/heads/$mparent") { + $mparent = get_headref($mparent, $git_dir); + push (@parents, $mparent); + print OUT "Merge parent branch: $mparent\n" if $opt_v; + } + } + } + my %seen_parents = (); + my @unique_parents = grep { ! $seen_parents{$_} ++ } @parents; + foreach my $bparent (@unique_parents) { + push @par, '-p', $bparent; + print OUT "Merge parent branch: $bparent\n" if $opt_v; + } + + exec("env", + "GIT_AUTHOR_NAME=$author_name", + "GIT_AUTHOR_EMAIL=$author_email", + "GIT_AUTHOR_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), + "GIT_COMMITTER_NAME=$committer_name", + "GIT_COMMITTER_EMAIL=$committer_email", + "GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), + "git-commit-tree", $tree,@par); + die "Cannot exec git-commit-tree: $!\n"; + } + $pw->writer(); + $pr->reader(); + + $message =~ s/[\s\n]+\z//; + $message = "r$revision: $message" if $opt_r; + + print $pw "$message\n" + or die "Error writing to git-commit-tree: $!\n"; + $pw->close(); + + print "Committed change $revision:$branch ".strftime("%Y-%m-%d %H:%M:%S",gmtime($date)).")\n" if $opt_v; + chomp($cid = <$pr>); + length($cid) == 40 + or die "Cannot get commit id ($cid): $!\n"; + print "Commit ID $cid\n" if $opt_v; + $pr->close(); + + waitpid($pid,0); + die "Error running git-commit-tree: $?\n" if $?; + } + + if (not defined $cid) { + $cid = $branches{"/"}{"LAST"}; + } + + if(not defined $dest) { + print "... no known parent\n" if $opt_v; + } elsif(not $tag) { + print "Writing to refs/heads/$dest\n" if $opt_v; + open(C,">$git_dir/refs/heads/$dest") and + print C ("$cid\n") and + close(C) + or die "Cannot write branch $dest for update: $!\n"; + } + + if ($tag) { + $last_rev = "-" if %$changed_paths; + # the tag was 'complex', i.e. did not refer to a "real" revision + + $dest =~ tr/_/\./ if $opt_u; + + system('git-tag', '-f', $dest, $cid) == 0 + or die "Cannot create tag $dest: $!\n"; + + print "Created tag '$dest' on '$branch'\n" if $opt_v; + } + $branches{$branch}{"LAST"} = $cid; + $branches{$branch}{$revision} = $cid; + $last_rev = $cid; + print BRANCHES "$revision $branch $cid\n"; + print "DONE: $revision $dest $cid\n" if $opt_v; +} + +sub commit_all { + # Recursive use of the SVN connection does not work + local $svn = $svn2; + + my ($changed_paths, $revision, $author, $date, $message) = @_; + my %p; + while(my($path,$action) = each %$changed_paths) { + $p{$path} = [ $action->action,$action->copyfrom_path, $action->copyfrom_rev, $path ]; + } + $changed_paths = \%p; + + my %done; + my @col; + my $pref; + my $branch; + + while(my($path,$action) = each %$changed_paths) { + ($branch,$path) = split_path($revision,$path); + next if not defined $branch; + next if not defined $path; + $done{$branch}{$path} = $action; + } + while(($branch,$changed_paths) = each %done) { + commit($branch, $changed_paths, $revision, $author, $date, $message); + } +} + +$opt_l = $svn->{'maxrev'} if not defined $opt_l or $opt_l > $svn->{'maxrev'}; + +if ($opt_l < $current_rev) { + print "Up to date: no new revisions to fetch!\n" if $opt_v; + unlink("$git_dir/SVN2GIT_HEAD"); + exit; +} + +print "Processing from $current_rev to $opt_l ...\n" if $opt_v; + +my $from_rev; +my $to_rev = $current_rev - 1; + +my $subpool = SVN::Pool::new_default_sub; +while ($to_rev < $opt_l) { + $subpool->clear; + $from_rev = $to_rev + 1; + $to_rev = $from_rev + $repack_after; + $to_rev = $opt_l if $opt_l < $to_rev; + print "Fetching from $from_rev to $to_rev ...\n" if $opt_v; + $svn->{'svn'}->get_log("/",$from_rev,$to_rev,0,1,1,\&commit_all); + my $pid = fork(); + die "Fork: $!\n" unless defined $pid; + unless($pid) { + exec("git-repack", "-d") + or die "Cannot repack: $!\n"; + } + waitpid($pid, 0); +} + + +unlink($git_index); + +if (defined $orig_git_index) { + $ENV{GIT_INDEX_FILE} = $orig_git_index; +} else { + delete $ENV{GIT_INDEX_FILE}; +} + +# Now switch back to the branch we were in before all of this happened +if($orig_branch) { + print "DONE\n" if $opt_v and (not defined $opt_l or $opt_l > 0); + system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") + if $forward_master; + unless ($opt_i) { + system('git-read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD'); + die "read-tree failed: $?\n" if $?; + } +} else { + $orig_branch = "master"; + print "DONE; creating $orig_branch branch\n" if $opt_v and (not defined $opt_l or $opt_l > 0); + system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") + unless -f "$git_dir/refs/heads/master"; + system('git-update-ref', 'HEAD', "$orig_branch"); + unless ($opt_i) { + system('git checkout'); + die "checkout failed: $?\n" if $?; + } +} +unlink("$git_dir/SVN2GIT_HEAD"); +close(BRANCHES); diff --git a/contrib/examples/git-svnimport.txt b/contrib/examples/git-svnimport.txt new file mode 100644 index 0000000000..71aad8b45b --- /dev/null +++ b/contrib/examples/git-svnimport.txt @@ -0,0 +1,179 @@ +git-svnimport(1) +================ +v0.1, July 2005 + +NAME +---- +git-svnimport - Import a SVN repository into git + + +SYNOPSIS +-------- +[verse] +'git-svnimport' [ -o ] [ -h ] [ -v ] [ -d | -D ] + [ -C ] [ -i ] [ -u ] [-l limit_rev] + [ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ] + [ -s start_chg ] [ -m ] [ -r ] [ -M regex ] + [ -I ] [ -A ] + [ -R ] [ -P ] + [ ] + + +DESCRIPTION +----------- +Imports a SVN repository into git. It will either create a new +repository, or incrementally import into an existing one. + +SVN access is done by the SVN::Perl module. + +git-svnimport assumes that SVN repositories are organized into one +"trunk" directory where the main development happens, "branches/FOO" +directories for branches, and "/tags/FOO" directories for tags. +Other subdirectories are ignored. + +git-svnimport creates a file ".git/svn2git", which is required for +incremental SVN imports. + +OPTIONS +------- +-C :: + The GIT repository to import to. If the directory doesn't + exist, it will be created. Default is the current directory. + +-s :: + Start importing at this SVN change number. The default is 1. ++ +When importing incrementally, you might need to edit the .git/svn2git file. + +-i:: + Import-only: don't perform a checkout after importing. This option + ensures the working directory and index remain untouched and will + not create them if they do not exist. + +-T :: + Name the SVN trunk. Default "trunk". + +-t :: + Name the SVN subdirectory for tags. Default "tags". + +-b :: + Name the SVN subdirectory for branches. Default "branches". + +-o :: + The 'trunk' branch from SVN is imported to the 'origin' branch within + the git repository. Use this option if you want to import into a + different branch. + +-r:: + Prepend 'rX: ' to commit messages, where X is the imported + subversion revision. + +-u:: + Replace underscores in tag names with periods. + +-I :: + Import the svn:ignore directory property to files with this + name in each directory. (The Subversion and GIT ignore + syntaxes are similar enough that using the Subversion patterns + directly with "-I .gitignore" will almost always just work.) + +-A :: + Read a file with lines on the form ++ +------ + username = User's Full Name + +------ ++ +and use "User's Full Name " as the GIT +author and committer for Subversion commits made by +"username". If encountering a commit made by a user not in the +list, abort. ++ +For convenience, this data is saved to $GIT_DIR/svn-authors +each time the -A option is provided, and read from that same +file each time git-svnimport is run with an existing GIT +repository without -A. + +-m:: + Attempt to detect merges based on the commit message. This option + will enable default regexes that try to capture the name source + branch name from the commit message. + +-M :: + Attempt to detect merges based on the commit message with a custom + regex. It can be used with -m to also see the default regexes. + You must escape forward slashes. + +-l :: + Specify a maximum revision number to pull. ++ +Formerly, this option controlled how many revisions to pull, +due to SVN memory leaks. (These have been worked around.) + +-R :: + Specify how often git repository should be repacked. ++ +The default value is 1000. git-svnimport will do import in chunks of 1000 +revisions, after each chunk git repository will be repacked. To disable +this behavior specify some big value here which is mote than number of +revisions to import. + +-P :: + Partial import of the SVN tree. ++ +By default, the whole tree on the SVN trunk (/trunk) is imported. +'-P my/proj' will import starting only from '/trunk/my/proj'. +This option is useful when you want to import one project from a +svn repo which hosts multiple projects under the same trunk. + +-v:: + Verbosity: let 'svnimport' report what it is doing. + +-d:: + Use direct HTTP requests if possible. The "" argument is used + only for retrieving the SVN logs; the path to the contents is + included in the SVN log. + +-D:: + Use direct HTTP requests if possible. The "" argument is used + for retrieving the logs, as well as for the contents. ++ +There's no safe way to automatically find out which of these options to +use, so you need to try both. Usually, the one that's wrong will die +with a 40x error pretty quickly. + +:: + The URL of the SVN module you want to import. For local + repositories, use "file:///absolute/path". ++ +If you're using the "-d" or "-D" option, this is the URL of the SVN +repository itself; it usually ends in "/svn". + +:: + The path to the module you want to check out. + +-h:: + Print a short usage message and exit. + +OUTPUT +------ +If '-v' is specified, the script reports what it is doing. + +Otherwise, success is indicated the Unix way, i.e. by simply exiting with +a zero exit status. + +Author +------ +Written by Matthias Urlichs , with help from +various participants of the git-list . + +Based on a cvs2git script by the same author. + +Documentation +-------------- +Documentation by Matthias Urlichs . + +GIT +--- +Part of the gitlink:git[7] suite -- cgit v1.2.3 From b43b0a3c5c313619397b2f81d5a4acad2c3cb4a9 Mon Sep 17 00:00:00 2001 From: Chris Pettitt Date: Thu, 1 Nov 2007 20:43:13 -0700 Subject: git-p4: Add a helper function to parse the full git diff-tree output. Signed-off-by: Chris Pettitt Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 49 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index bf33f74b70..c7fc564a5b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -71,6 +71,46 @@ def isP4Exec(kind): a plus sign, it is also executable""" return (re.search(r"(^[cku]?x)|\+.*x", kind) != None) +def diffTreePattern(): + # This is a simple generator for the diff tree regex pattern. This could be + # a class variable if this and parseDiffTreeEntry were a part of a class. + pattern = re.compile(':(\d+) (\d+) (\w+) (\w+) ([A-Z])(\d+)?\t(.*?)((\t(.*))|$)') + while True: + yield pattern + +def parseDiffTreeEntry(entry): + """Parses a single diff tree entry into its component elements. + + See git-diff-tree(1) manpage for details about the format of the diff + output. This method returns a dictionary with the following elements: + + src_mode - The mode of the source file + dst_mode - The mode of the destination file + src_sha1 - The sha1 for the source file + dst_sha1 - The sha1 fr the destination file + status - The one letter status of the diff (i.e. 'A', 'M', 'D', etc) + status_score - The score for the status (applicable for 'C' and 'R' + statuses). This is None if there is no score. + src - The path for the source file. + dst - The path for the destination file. This is only present for + copy or renames. If it is not present, this is None. + + If the pattern is not matched, None is returned.""" + + match = diffTreePattern().next().match(entry) + if match: + return { + 'src_mode': match.group(1), + 'dst_mode': match.group(2), + 'src_sha1': match.group(3), + 'dst_sha1': match.group(4), + 'status': match.group(5), + 'status_score': match.group(6), + 'src': match.group(7), + 'dst': match.group(10) + } + return None + def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: @@ -494,13 +534,14 @@ class P4Submit(Command): else: print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) diffOpts = ("", "-M")[self.detectRename] - diff = read_pipe_lines("git diff-tree -r --name-status %s \"%s^\" \"%s\"" % (diffOpts, id, id)) + diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id)) filesToAdd = set() filesToDelete = set() editedFiles = set() for line in diff: - modifier = line[0] - path = line[1:].strip() + diff = parseDiffTreeEntry(line) + modifier = diff['status'] + path = diff['src'] if modifier == "M": system("p4 edit \"%s\"" % path) editedFiles.add(path) @@ -513,7 +554,7 @@ class P4Submit(Command): if path in filesToAdd: filesToAdd.remove(path) elif modifier == "R": - src, dest = line.strip().split("\t")[1:3] + src, dest = diff['src'], diff['dst'] system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) system("p4 edit \"%s\"" % (dest)) os.unlink(dest) -- cgit v1.2.3 From c65b670e858a0fc010c8b8bb3cbf064bb22e1839 Mon Sep 17 00:00:00 2001 From: Chris Pettitt Date: Thu, 1 Nov 2007 20:43:14 -0700 Subject: git-p4: Detect changes to executable bit and include them in p4 submit. This changeset takes advantage of the new parseDiffTreeEntry(...) function to detect changes to the execute bit in the git repository. During submit, git-p4 now looks for changes to the executable bit and if it finds them it "reopens" the file in perforce, which allows it to change the file type. The logic for adding the executable bit in perforce is straightforward: the +x modifier can be used. Removing the executable bit in perforce requires that the entire filetype be redefined (there is no way to join remove the bit with a -x modifier, for example). This changeset includes logic to remove the executable bit from the full file type while preserving the base file type and other modifiers. Signed-off-by: Chris Pettitt Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c7fc564a5b..c148b5ab7d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -71,6 +71,31 @@ def isP4Exec(kind): a plus sign, it is also executable""" return (re.search(r"(^[cku]?x)|\+.*x", kind) != None) +def setP4ExecBit(file, mode): + # Reopens an already open file and changes the execute bit to match + # the execute bit setting in the passed in mode. + + p4Type = "+x" + + if not isModeExec(mode): + p4Type = getP4OpenedType(file) + p4Type = re.sub('^([cku]?)x(.*)', '\\1\\2', p4Type) + p4Type = re.sub('(.*?\+.*?)x(.*?)', '\\1\\2', p4Type) + if p4Type[-1] == "+": + p4Type = p4Type[0:-1] + + system("p4 reopen -t %s %s" % (p4Type, file)) + +def getP4OpenedType(file): + # Returns the perforce file type for the given file. + + result = read_pipe("p4 opened %s" % file) + match = re.match(".*\((.+)\)$", result) + if match: + return match.group(1) + else: + die("Could not determine file type for %s" % file) + def diffTreePattern(): # This is a simple generator for the diff tree regex pattern. This could be # a class variable if this and parseDiffTreeEntry were a part of a class. @@ -111,6 +136,14 @@ def parseDiffTreeEntry(entry): } return None +def isModeExec(mode): + # Returns True if the given git mode represents an executable file, + # otherwise False. + return mode[-3:] == "755" + +def isModeExecChanged(src_mode, dst_mode): + return isModeExec(src_mode) != isModeExec(dst_mode) + def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): cmd = "p4 -G %s" % cmd if verbose: @@ -538,15 +571,19 @@ class P4Submit(Command): filesToAdd = set() filesToDelete = set() editedFiles = set() + filesToChangeExecBit = {} for line in diff: diff = parseDiffTreeEntry(line) modifier = diff['status'] path = diff['src'] if modifier == "M": system("p4 edit \"%s\"" % path) + if isModeExecChanged(diff['src_mode'], diff['dst_mode']): + filesToChangeExecBit[path] = diff['dst_mode'] editedFiles.add(path) elif modifier == "A": filesToAdd.add(path) + filesToChangeExecBit[path] = diff['dst_mode'] if path in filesToDelete: filesToDelete.remove(path) elif modifier == "D": @@ -557,6 +594,8 @@ class P4Submit(Command): src, dest = diff['src'], diff['dst'] system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) system("p4 edit \"%s\"" % (dest)) + if isModeExecChanged(diff['src_mode'], diff['dst_mode']): + filesToChangeExecBit[dest] = diff['dst_mode'] os.unlink(dest) editedFiles.add(dest) filesToDelete.add(src) @@ -609,6 +648,11 @@ class P4Submit(Command): system("p4 revert \"%s\"" % f) system("p4 delete \"%s\"" % f) + # Set/clear executable bits + for f in filesToChangeExecBit.keys(): + mode = filesToChangeExecBit[f] + setP4ExecBit(f, mode) + logMessage = "" if not self.directSubmit: logMessage = extractLogMessageFromGitCommit(id) -- cgit v1.2.3 From 8951d7c1f1ae38f34617b6c2490bf65e73e371f7 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 4 Nov 2007 15:51:17 -0500 Subject: Build in ls-remote This actually replaces peek-remote with ls-remote, since peek-remote now handles everything. peek-remote remains an a second name for ls-remote, although its help message now gives the "ls-remote" name. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- contrib/examples/git-ls-remote.sh | 142 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100755 contrib/examples/git-ls-remote.sh (limited to 'contrib') diff --git a/contrib/examples/git-ls-remote.sh b/contrib/examples/git-ls-remote.sh new file mode 100755 index 0000000000..fec70bbf88 --- /dev/null +++ b/contrib/examples/git-ls-remote.sh @@ -0,0 +1,142 @@ +#!/bin/sh +# + +usage () { + echo >&2 "usage: $0 [--heads] [--tags] [-u|--upload-pack ]" + echo >&2 " ..." + exit 1; +} + +die () { + echo >&2 "$*" + exit 1 +} + +exec= +while test $# != 0 +do + case "$1" in + -h|--h|--he|--hea|--head|--heads) + heads=heads; shift ;; + -t|--t|--ta|--tag|--tags) + tags=tags; shift ;; + -u|--u|--up|--upl|--uploa|--upload|--upload-|--upload-p|--upload-pa|\ + --upload-pac|--upload-pack) + shift + exec="--upload-pack=$1" + shift;; + -u=*|--u=*|--up=*|--upl=*|--uplo=*|--uploa=*|--upload=*|\ + --upload-=*|--upload-p=*|--upload-pa=*|--upload-pac=*|--upload-pack=*) + exec=--upload-pack=$(expr "z$1" : 'z-[^=]*=\(.*\)') + shift;; + --) + shift; break ;; + -*) + usage ;; + *) + break ;; + esac +done + +case "$#" in 0) usage ;; esac + +case ",$heads,$tags," in +,,,) heads=heads tags=tags other=other ;; +esac + +. git-parse-remote +peek_repo="$(get_remote_url "$@")" +shift + +tmp=.ls-remote-$$ +trap "rm -fr $tmp-*" 0 1 2 3 15 +tmpdir=$tmp-d + +case "$peek_repo" in +http://* | https://* | ftp://* ) + if [ -n "$GIT_SSL_NO_VERIFY" -o \ + "`git config --bool http.sslVerify`" = false ]; then + curl_extra_args="-k" + fi + if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \ + "`git config --bool http.noEPSV`" = true ]; then + curl_extra_args="${curl_extra_args} --disable-epsv" + fi + curl -nsf $curl_extra_args --header "Pragma: no-cache" "$peek_repo/info/refs" || + echo "failed slurping" + ;; + +rsync://* ) + mkdir $tmpdir && + rsync -rlq "$peek_repo/HEAD" $tmpdir && + rsync -rq "$peek_repo/refs" $tmpdir || { + echo "failed slurping" + exit + } + head=$(cat "$tmpdir/HEAD") && + case "$head" in + ref:' '*) + head=$(expr "z$head" : 'zref: \(.*\)') && + head=$(cat "$tmpdir/$head") || exit + esac && + echo "$head HEAD" + (cd $tmpdir && find refs -type f) | + while read path + do + tr -d '\012' <"$tmpdir/$path" + echo " $path" + done && + rm -fr $tmpdir + ;; + +* ) + if test -f "$peek_repo" ; then + git bundle list-heads "$peek_repo" || + echo "failed slurping" + else + git-peek-remote $exec "$peek_repo" || + echo "failed slurping" + fi + ;; +esac | +sort -t ' ' -k 2 | +while read sha1 path +do + case "$sha1" in + failed) + exit 1 ;; + esac + case "$path" in + refs/heads/*) + group=heads ;; + refs/tags/*) + group=tags ;; + *) + group=other ;; + esac + case ",$heads,$tags,$other," in + *,$group,*) + ;; + *) + continue;; + esac + case "$#" in + 0) + match=yes ;; + *) + match=no + for pat + do + case "/$path" in + */$pat ) + match=yes + break ;; + esac + done + esac + case "$match" in + no) + continue ;; + esac + echo "$sha1 $path" +done -- cgit v1.2.3 From b5786c828304c7dcf42742729374d04f30ac1a09 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 6 Nov 2007 13:48:07 +0000 Subject: contrib/hooks/post-receive-email: fix typo Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 2aa9bb501c..379cedc577 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -30,7 +30,7 @@ # This is the list that all pushes of annotated tags will go to. Leave it # blank to default to the mailinglist field. The announce emails lists the # short log summary of the changes since the last annotated tag. -# hook.envelopesender +# hooks.envelopesender # If set then the -f option is passed to sendmail to allow the envelope sender # address to be set # -- cgit v1.2.3 From 15a2f530111f32d66eb0c25527e540b5e09804ad Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 6 Nov 2007 13:48:34 +0000 Subject: contrib/hooks/post-receive-email: reformat to wrap comments at 76 chars Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 199 +++++++++++++++++++++------------------ 1 file changed, 107 insertions(+), 92 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 379cedc577..9b9a977771 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -2,24 +2,26 @@ # # Copyright (c) 2007 Andy Parkins # -# An example hook script to mail out commit update information. This hook sends emails -# listing new revisions to the repository introduced by the change being reported. The -# rule is that (for branch updates) each commit will appear on one email and one email -# only. +# An example hook script to mail out commit update information. This hook +# sends emails listing new revisions to the repository introduced by the +# change being reported. The rule is that (for branch updates) each commit +# will appear on one email and one email only. # -# This hook is stored in the contrib/hooks directory. Your distribution will have put -# this somewhere standard. You should make this script executable then link to it in -# the repository you would like to use it in. For example, on debian the hook is stored -# in /usr/share/doc/git-core/contrib/hooks/post-receive-email: +# This hook is stored in the contrib/hooks directory. Your distribution +# will have put this somewhere standard. You should make this script +# executable then link to it in the repository you would like to use it in. +# For example, on debian the hook is stored in +# /usr/share/doc/git-core/contrib/hooks/post-receive-email: # # chmod a+x post-receive-email # cd /path/to/your/repository.git # ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive # -# This hook script assumes it is enabled on the central repository of a project, with -# all users pushing only to it and not between each other. It will still work if you -# don't operate in that style, but it would become possible for the email to be from -# someone other than the person doing the push. +# This hook script assumes it is enabled on the central repository of a +# project, with all users pushing only to it and not between each other. It +# will still work if you don't operate in that style, but it would become +# possible for the email to be from someone other than the person doing the +# push. # # Config # ------ @@ -28,11 +30,11 @@ # emails for every ref update. # hooks.announcelist # This is the list that all pushes of annotated tags will go to. Leave it -# blank to default to the mailinglist field. The announce emails lists the -# short log summary of the changes since the last annotated tag. +# blank to default to the mailinglist field. The announce emails lists +# the short log summary of the changes since the last annotated tag. # hooks.envelopesender -# If set then the -f option is passed to sendmail to allow the envelope sender -# address to be set +# If set then the -f option is passed to sendmail to allow the envelope +# sender address to be set # # Notes # ----- @@ -49,8 +51,8 @@ # this is and calls the appropriate body-generation routine after outputting # the common header # -# Note this function doesn't actually generate any email output, that is taken -# care of by the functions it calls: +# Note this function doesn't actually generate any email output, that is +# taken care of by the functions it calls: # - generate_email_header # - generate_create_XXXX_email # - generate_update_XXXX_email @@ -225,8 +227,9 @@ generate_create_branch_email() echo $LOGBEGIN # This shows all log entries that are not already covered by # another ref - i.e. commits that are now accessible from this - # ref that were previously not accessible (see generate_update_branch_email - # for the explanation of this command) + # ref that were previously not accessible + # (see generate_update_branch_email for the explanation of this + # command) git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-list --pretty --stdin $newrev echo $LOGEND @@ -254,9 +257,10 @@ generate_update_branch_email() # # git-rev-list N ^O ^X ^N # - # So, we need to build up the list more carefully. git-rev-parse will - # generate a list of revs that may be fed into git-rev-list. We can get - # it to make the "--not --all" part and then filter out the "^N" with: + # So, we need to build up the list more carefully. git-rev-parse + # will generate a list of revs that may be fed into git-rev-list. + # We can get it to make the "--not --all" part and then filter out + # the "^N" with: # # git-rev-parse --not --all | grep -v N # @@ -266,16 +270,17 @@ generate_update_branch_email() # git-rev-list N ^O ^X # # This leaves a problem when someone else updates the repository - # while this script is running. Their new value of the ref we're working - # on would be included in the "--not --all" output; and as our $newrev - # would be an ancestor of that commit, it would exclude all of our - # commits. What we really want is to exclude the current value of - # $refname from the --not list, rather than N itself. So: + # while this script is running. Their new value of the ref we're + # working on would be included in the "--not --all" output; and as + # our $newrev would be an ancestor of that commit, it would exclude + # all of our commits. What we really want is to exclude the current + # value of $refname from the --not list, rather than N itself. So: # # git-rev-parse --not --all | grep -v $(git-rev-parse $refname) # - # Get's us to something pretty safe (apart from the small time between - # refname being read, and git-rev-parse running - for that, I give up) + # Get's us to something pretty safe (apart from the small time + # between refname being read, and git-rev-parse running - for that, + # I give up) # # # Next problem, consider this: @@ -283,18 +288,18 @@ generate_update_branch_email() # \ # * --- X --- * --- N ($newrev) # - # That is to say, there is no guarantee that oldrev is a strict subset of - # newrev (it would have required a --force, but that's allowed). So, we - # can't simply say rev-list $oldrev..$newrev. Instead we find the common - # base of the two revs and list from there. + # That is to say, there is no guarantee that oldrev is a strict + # subset of newrev (it would have required a --force, but that's + # allowed). So, we can't simply say rev-list $oldrev..$newrev. + # Instead we find the common base of the two revs and list from + # there. # - # As above, we need to take into account the presence of X; if another - # branch is already in the repository and points at some of the revisions - # that we are about to output - we don't want them. The solution is as - # before: git-rev-parse output filtered. + # As above, we need to take into account the presence of X; if + # another branch is already in the repository and points at some of + # the revisions that we are about to output - we don't want them. + # The solution is as before: git-rev-parse output filtered. # - # Finally, tags: - # 1 --- 2 --- O --- T --- 3 --- 4 --- N + # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N # # Tags pushed into the repository generate nice shortlog emails that # summarise the commits between them and the previous tag. However, @@ -302,13 +307,14 @@ generate_update_branch_email() # for a branch update. Therefore we still want to output revisions # that have been output on a tag email. # - # Luckily, git-rev-parse includes just the tool. Instead of using "--all" - # we use "--branches"; this has the added benefit that "remotes/" will - # be ignored as well. - - # List all of the revisions that were removed by this update, in a fast forward - # update, this list will be empty, because rev-list O ^N is empty. For a non - # fast forward, O ^N is the list of removed revisions + # Luckily, git-rev-parse includes just the tool. Instead of using + # "--all" we use "--branches"; this has the added benefit that + # "remotes/" will be ignored as well. + + # List all of the revisions that were removed by this update, in a + # fast forward update, this list will be empty, because rev-list O + # ^N is empty. For a non fast forward, O ^N is the list of removed + # revisions fast_forward="" rev="" for rev in $(git rev-list $newrev..$oldrev) @@ -321,10 +327,10 @@ generate_update_branch_email() fi # List all the revisions from baserev to newrev in a kind of - # "table-of-contents"; note this list can include revisions that have - # already had notification emails and is present to show the full detail - # of the change from rolling back the old revision to the base revision and - # then forward to the new revision + # "table-of-contents"; note this list can include revisions that + # have already had notification emails and is present to show the + # full detail of the change from rolling back the old revision to + # the base revision and then forward to the new revision for rev in $(git rev-list $oldrev..$newrev) do revtype=$(git cat-file -t "$rev") @@ -334,19 +340,20 @@ generate_update_branch_email() if [ "$fast_forward" ]; then echo " from $oldrev ($oldrev_type)" else - # 1. Existing revisions were removed. In this case newrev is a - # subset of oldrev - this is the reverse of a fast-forward, - # a rewind - # 2. New revisions were added on top of an old revision, this is - # a rewind and addition. + # 1. Existing revisions were removed. In this case newrev + # is a subset of oldrev - this is the reverse of a + # fast-forward, a rewind + # 2. New revisions were added on top of an old revision, + # this is a rewind and addition. - # (1) certainly happened, (2) possibly. When (2) hasn't happened, - # we set a flag to indicate that no log printout is required. + # (1) certainly happened, (2) possibly. When (2) hasn't + # happened, we set a flag to indicate that no log printout + # is required. echo "" - # Find the common ancestor of the old and new revisions and compare - # it with newrev + # Find the common ancestor of the old and new revisions and + # compare it with newrev baserev=$(git merge-base $oldrev $newrev) rewind_only="" if [ "$baserev" = "$newrev" ]; then @@ -387,21 +394,22 @@ generate_update_branch_email() git rev-parse --not --branches | grep -v $(git rev-parse $refname) | git rev-list --pretty --stdin $oldrev..$newrev - # XXX: Need a way of detecting whether git rev-list actually outputted - # anything, so that we can issue a "no new revisions added by this - # update" message + # XXX: Need a way of detecting whether git rev-list actually + # outputted anything, so that we can issue a "no new + # revisions added by this update" message echo $LOGEND else echo "No new revisions were added by this update." fi - # The diffstat is shown from the old revision to the new revision. This - # is to show the truth of what happened in this change. There's no point - # showing the stat from the base to the new revision because the base - # is effectively a random revision at this point - the user will be - # interested in what this revision changed - including the undoing of - # previous revisions in the case of non-fast forward updates. + # The diffstat is shown from the old revision to the new revision. + # This is to show the truth of what happened in this change. + # There's no point showing the stat from the base to the new + # revision because the base is effectively a random revision at this + # point - the user will be interested in what this revision changed + # - including the undoing of previous revisions in the case of + # non-fast forward updates. echo "" echo "Summary of changes:" git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev @@ -448,7 +456,8 @@ generate_update_atag_email() # generate_atag_email() { - # Use git-for-each-ref to pull out the individual fields from the tag + # Use git-for-each-ref to pull out the individual fields from the + # tag eval $(git for-each-ref --shell --format=' tagobject=%(*objectname) tagtype=%(*objecttype) @@ -459,8 +468,10 @@ generate_atag_email() echo " tagging $tagobject ($tagtype)" case "$tagtype" in commit) + # If the tagged object is a commit, then we assume this is a - # release, and so we calculate which tag this tag is replacing + # release, and so we calculate which tag this tag is + # replacing prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null) if [ -n "$prevtag" ]; then @@ -477,25 +488,27 @@ generate_atag_email() echo "" echo $LOGBEGIN - # Show the content of the tag message; this might contain a change log - # or release notes so is worth displaying. + # Show the content of the tag message; this might contain a change + # log or release notes so is worth displaying. git cat-file tag $newrev | sed -e '1,/^$/d' echo "" case "$tagtype" in commit) - # Only commit tags make sense to have rev-list operations performed - # on them + # Only commit tags make sense to have rev-list operations + # performed on them if [ -n "$prevtag" ]; then # Show changes since the previous release git rev-list --pretty=short "$prevtag..$newrev" | git shortlog else - # No previous tag, show all the changes since time began + # No previous tag, show all the changes since time + # began git rev-list --pretty=short $newrev | git shortlog fi ;; *) - # XXX: Is there anything useful we can do for non-commit objects? + # XXX: Is there anything useful we can do for non-commit + # objects? ;; esac @@ -544,13 +557,14 @@ generate_update_general_email() # generate_general_email() { - # Unannotated tags are more about marking a point than releasing a version; - # therefore we don't do the shortlog summary that we do for annotated tags - # above - we simply show that the point has been marked, and print the log - # message for the marked point for reference purposes + # Unannotated tags are more about marking a point than releasing a + # version; therefore we don't do the shortlog summary that we do for + # annotated tags above - we simply show that the point has been + # marked, and print the log message for the marked point for + # reference purposes # - # Note this section also catches any other reference type (although there - # aren't any) and deals with them in the same way. + # Note this section also catches any other reference type (although + # there aren't any) and deals with them in the same way. echo "" if [ "$newrev_type" = "commit" ]; then @@ -558,10 +572,10 @@ generate_general_email() git show --no-color --root -s $newrev echo $LOGEND else - # What can we do here? The tag marks an object that is not a commit, - # so there is no log for us to display. It's probably not wise to - # output git-cat-file as it could be a binary blob. We'll just say how - # big it is + # What can we do here? The tag marks an object that is not + # a commit, so there is no log for us to display. It's + # probably not wise to output git-cat-file as it could be a + # binary blob. We'll just say how big it is echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." fi } @@ -604,8 +618,8 @@ if [ -z "$GIT_DIR" ]; then fi projectdesc=$(sed -ne '1p' "$GIT_DIR/description") -# Check if the description is unchanged from it's default, and shorten it to a -# more manageable length if it is +# Check if the description is unchanged from it's default, and shorten it to +# a more manageable length if it is if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null then projectdesc="UNNAMED PROJECT" @@ -616,11 +630,12 @@ announcerecipients=$(git repo-config hooks.announcelist) envelopesender=$(git-repo-config hooks.envelopesender) # --- Main loop -# Allow dual mode: run from the command line just like the update hook, or if -# no arguments are given then run as a hook script +# Allow dual mode: run from the command line just like the update hook, or +# if no arguments are given then run as a hook script if [ -n "$1" -a -n "$2" -a -n "$3" ]; then # Output to the terminal in command line mode - if someone wanted to - # resend an email; they could redirect the output to sendmail themselves + # resend an email; they could redirect the output to sendmail + # themselves PAGER= generate_email $2 $3 $1 else while read oldrev newrev refname -- cgit v1.2.3 From e7509ee388480046a685f885431291f484de66de Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Tue, 6 Nov 2007 13:49:30 +0000 Subject: contrib/hooks/post-receive-email: make subject prefix configurable Email subjects are prefixed with "[SCM] " by default, make this optionally configurable through the hooks.emailprefix config option. Suggested by martin f krafft through http://bugs.debian.org/428418 Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 9b9a977771..3904c182e7 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -35,10 +35,12 @@ # hooks.envelopesender # If set then the -f option is passed to sendmail to allow the envelope # sender address to be set +# hooks.emailprefix +# All emails have their subjects prefixed with this prefix, or "[SCM]" +# if emailprefix is unset, to aid filtering # # Notes # ----- -# All emails have their subjects prefixed with "[SCM]" to aid filtering. # All emails include the headers "X-Git-Refname", "X-Git-Oldrev", # "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and # give information for debugging. @@ -188,7 +190,7 @@ generate_email_header() # Generate header cat <<-EOF To: $recipients - Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe + Subject: ${emailprefix}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe X-Git-Refname: $refname X-Git-Reftype: $refname_type X-Git-Oldrev: $oldrev @@ -604,7 +606,6 @@ send_mail() # ---------------------------- main() # --- Constants -EMAILPREFIX="[SCM] " LOGBEGIN="- Log -----------------------------------------------------------------" LOGEND="-----------------------------------------------------------------------" @@ -628,6 +629,7 @@ fi recipients=$(git repo-config hooks.mailinglist) announcerecipients=$(git repo-config hooks.announcelist) envelopesender=$(git-repo-config hooks.envelopesender) +emailprefix=$(git-repo-config hooks.emailprefix || echo '[SCM] ') # --- Main loop # Allow dual mode: run from the command line just like the update hook, or -- cgit v1.2.3 From 5c355059c379a55366bed573d4e65dba4b5a0565 Mon Sep 17 00:00:00 2001 From: Gerrit Pape Date: Thu, 8 Nov 2007 12:11:57 +0000 Subject: contrib/hooks/post-receive-email: remove cruft, $committer is not used Signed-off-by: Gerrit Pape Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 4 ---- 1 file changed, 4 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 3904c182e7..7511ea0797 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -156,10 +156,6 @@ generate_email() fi # Email parameters - # The committer will be obtained from the latest existing rev; so - # for a deletion it will be the oldrev, for the others, then newrev - committer=$(git show --pretty=full -s $rev | sed -ne "s/^Commit: //p" | - sed -ne 's/\(.*\) /dev/null) -- cgit v1.2.3 From 38f9f5ec41803558b4ccefc9db08fac5696aff8d Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 15 Nov 2007 10:38:45 +0100 Subject: git-p4: Fix direct import from perforce after fetching changes through git from origin When using an existing git repository to cache the perforce import we don't fetch the branch mapping from perforce as that is a slow operation. However the origin repository may not be fully up-to-date and therefore it may be necessary to import more changes directly from Perforce. Such a direct import needs self.knownBranches to be set up though, so initialize it from the existing p4/* git branches. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c148b5ab7d..c869bb8864 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1207,6 +1207,15 @@ class P4Sync(Command): for branch in lostAndFoundBranches: self.knownBranches[branch] = branch + def getBranchMappingFromGitBranches(self): + branches = p4BranchesInGit(self.importIntoRemotes) + for branch in branches.keys(): + if branch == "master": + branch = "main" + else: + branch = branch[len(self.projectName):] + self.knownBranches[branch] = branch + def listExistingP4GitBranches(self): # branches holds mapping from name to commit branches = p4BranchesInGit(self.importIntoRemotes) @@ -1541,8 +1550,10 @@ class P4Sync(Command): ## FIXME - what's a P4 projectName ? self.projectName = self.guessProjectName() - if not self.hasOrigin: - self.getBranchMapping(); + if self.hasOrigin: + self.getBranchMappingFromGitBranches() + else: + self.getBranchMapping() if self.verbose: print "p4-git branches: %s" % self.p4BranchesInGit print "initial parents: %s" % self.initialParents -- cgit v1.2.3 From 113f10f22f4b3b599e44e192e241e0bace9cc39e Mon Sep 17 00:00:00 2001 From: Shawn Bohrer Date: Sun, 11 Nov 2007 19:48:47 -0600 Subject: Make git-clean a builtin This replaces git-clean.sh with builtin-clean.c, and moves git-clean.sh to the examples. This also introduces a change in behavior when removing directories explicitly specified as a path. For example currently: 1. When dir has only untracked files, these two behave differently: $ git clean -n dir $ git clean -n dir/ the former says "Would not remove dir/", while the latter would say "Would remove dir/untracked" for all paths under it, but not the directory itself. With -d, the former would stop refusing, however since the user explicitly asked to remove the directory the -d is no longer required. 2. When there are more parameters: $ git clean -n dir foo $ git clean -n dir/ foo both cases refuse to remove dir/ unless -d is specified. Once again since both cases requested to remove dir the -d is no longer required. Thanks to Johannes Schindelin for the conversion to using the parse-options API. Signed-off-by: Shawn Bohrer Signed-off-by: Junio C Hamano --- contrib/examples/git-clean.sh | 118 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100755 contrib/examples/git-clean.sh (limited to 'contrib') diff --git a/contrib/examples/git-clean.sh b/contrib/examples/git-clean.sh new file mode 100755 index 0000000000..01c95e9fe8 --- /dev/null +++ b/contrib/examples/git-clean.sh @@ -0,0 +1,118 @@ +#!/bin/sh +# +# Copyright (c) 2005-2006 Pavel Roskin +# + +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git-clean [options] ... + +Clean untracked files from the working directory + +When optional ... arguments are given, the paths +affected are further limited to those that match them. +-- +d remove directories as well +f override clean.requireForce and clean anyway +n don't remove anything, just show what would be done +q be quiet, only report errors +x remove ignored files as well +X remove only ignored files" + +SUBDIRECTORY_OK=Yes +. git-sh-setup +require_work_tree + +ignored= +ignoredonly= +cleandir= +rmf="rm -f --" +rmrf="rm -rf --" +rm_refuse="echo Not removing" +echo1="echo" + +disabled=$(git config --bool clean.requireForce) + +while test $# != 0 +do + case "$1" in + -d) + cleandir=1 + ;; + -f) + disabled=false + ;; + -n) + disabled=false + rmf="echo Would remove" + rmrf="echo Would remove" + rm_refuse="echo Would not remove" + echo1=":" + ;; + -q) + echo1=":" + ;; + -x) + ignored=1 + ;; + -X) + ignoredonly=1 + ;; + --) + shift + break + ;; + *) + usage # should not happen + ;; + esac + shift +done + +# requireForce used to default to false but now it defaults to true. +# IOW, lack of explicit "clean.requireForce = false" is taken as +# "clean.requireForce = true". +case "$disabled" in +"") + die "clean.requireForce not set and -n or -f not given; refusing to clean" + ;; +"true") + die "clean.requireForce set and -n or -f not given; refusing to clean" + ;; +esac + +if [ "$ignored,$ignoredonly" = "1,1" ]; then + die "-x and -X cannot be set together" +fi + +if [ -z "$ignored" ]; then + excl="--exclude-per-directory=.gitignore" + excl_info= excludes_file= + if [ -f "$GIT_DIR/info/exclude" ]; then + excl_info="--exclude-from=$GIT_DIR/info/exclude" + fi + if cfg_excl=$(git config core.excludesfile) && test -f "$cfg_excl" + then + excludes_file="--exclude-from=$cfg_excl" + fi + if [ "$ignoredonly" ]; then + excl="$excl --ignored" + fi +fi + +git ls-files --others --directory \ + $excl ${excl_info:+"$excl_info"} ${excludes_file:+"$excludes_file"} \ + -- "$@" | +while read -r file; do + if [ -d "$file" -a ! -L "$file" ]; then + if [ -z "$cleandir" ]; then + $rm_refuse "$file" + continue + fi + $echo1 "Removing $file" + $rmrf "$file" + else + $echo1 "Removing $file" + $rmf "$file" + fi +done -- cgit v1.2.3 From 59adeef48fcc3ea3e1288ce62260fdd8f46240da Mon Sep 17 00:00:00 2001 From: Anton Gyllenberg Date: Mon, 19 Nov 2007 12:37:16 +0200 Subject: gitview: import only one of gtksourceview and gtksourceview2 Importing both gtksourceview and gtksourceview2 will make python segfault on my system (ubuntu 7.10). Change so that gtksourceview is only imported if importing gtksourceview2 fails. This should be safe as gtksourceview is only used if gtksourceview2 is not available. Signed-off-by: Anton Gyllenberg Signed-off-by: Junio C Hamano --- contrib/gitview/gitview | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 449ee69bf4..4c99dfb903 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -27,20 +27,17 @@ import math import string import fcntl +have_gtksourceview2 = False +have_gtksourceview = False try: import gtksourceview2 have_gtksourceview2 = True except ImportError: - have_gtksourceview2 = False - -try: - import gtksourceview - have_gtksourceview = True -except ImportError: - have_gtksourceview = False - -if not have_gtksourceview2 and not have_gtksourceview: - print "Running without gtksourceview2 or gtksourceview module" + try: + import gtksourceview + have_gtksourceview = True + except ImportError: + print "Running without gtksourceview2 or gtksourceview module" re_ident = re.compile('(author|committer) (?P.*) (?P\d+) (?P[+-]\d{4})') -- cgit v1.2.3 From 183f84365de7b4b1fe0e15cebce80a95023aa1d6 Mon Sep 17 00:00:00 2001 From: Shun Kei Leung Date: Wed, 21 Nov 2007 11:01:19 +0800 Subject: git-p4: Fix typo in --detect-labels Signed-off-by: Kevin Leung Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c869bb8864..c80a6da252 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1141,7 +1141,7 @@ class P4Sync(Command): l = p4CmdList("labels %s..." % ' '.join (self.depotPaths)) if len(l) > 0 and not self.silent: - print "Finding files belonging to labels in %s" % `self.depotPath` + print "Finding files belonging to labels in %s" % `self.depotPaths` for output in l: label = output["label"] -- cgit v1.2.3 From a00a42ae33708caa742d9e9fbf10692cfa42f032 Mon Sep 17 00:00:00 2001 From: Thomas Harning Date: Thu, 22 Nov 2007 15:19:40 -0500 Subject: git-merge-ours: make it a builtin. Except that this fixes a longstanding corner case bug by tightening the way underlying diff-index command is run, it is functionally equivalent to the scripted version. Signed-off-by: Thomas Harning Jr Signed-off-by: Junio C Hamano --- contrib/examples/git-merge-ours.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100755 contrib/examples/git-merge-ours.sh (limited to 'contrib') diff --git a/contrib/examples/git-merge-ours.sh b/contrib/examples/git-merge-ours.sh new file mode 100755 index 0000000000..c81a790aa6 --- /dev/null +++ b/contrib/examples/git-merge-ours.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# +# Pretend we resolved the heads, but declare our tree trumps everybody else. +# + +# We need to exit with 2 if the index does not match our HEAD tree, +# because the current index is what we will be committing as the +# merge result. + +git diff-index --quiet --cached HEAD || exit 2 + +exit 0 -- cgit v1.2.3 From f5bbc3225c4b073a7ff3218164a0c820299bc9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20H=C3=B8gsberg?= Date: Thu, 8 Nov 2007 11:59:00 -0500 Subject: Port git commit to C. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes git commit a builtin and moves git-commit.sh to contrib/examples. This also removes the git-runstatus helper, which was mostly just a git-status.sh implementation detail. Signed-off-by: Kristian Høgsberg Signed-off-by: Junio C Hamano --- contrib/examples/git-commit.sh | 629 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 629 insertions(+) create mode 100755 contrib/examples/git-commit.sh (limited to 'contrib') diff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh new file mode 100755 index 0000000000..485339754c --- /dev/null +++ b/contrib/examples/git-commit.sh @@ -0,0 +1,629 @@ +#!/bin/sh +# +# Copyright (c) 2005 Linus Torvalds +# Copyright (c) 2006 Junio C Hamano + +USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m | -F | (-C|-c) | --amend] [-u] [-e] [--author ] [--template ] [[-i | -o] ...]' +SUBDIRECTORY_OK=Yes +OPTIONS_SPEC= +. git-sh-setup +require_work_tree + +git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t + +case "$0" in +*status) + status_only=t + ;; +*commit) + status_only= + ;; +esac + +refuse_partial () { + echo >&2 "$1" + echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?" + exit 1 +} + +TMP_INDEX= +THIS_INDEX="${GIT_INDEX_FILE:-$GIT_DIR/index}" +NEXT_INDEX="$GIT_DIR/next-index$$" +rm -f "$NEXT_INDEX" +save_index () { + cp -p "$THIS_INDEX" "$NEXT_INDEX" +} + +run_status () { + # If TMP_INDEX is defined, that means we are doing + # "--only" partial commit, and that index file is used + # to build the tree for the commit. Otherwise, if + # NEXT_INDEX exists, that is the index file used to + # make the commit. Otherwise we are using as-is commit + # so the regular index file is what we use to compare. + if test '' != "$TMP_INDEX" + then + GIT_INDEX_FILE="$TMP_INDEX" + export GIT_INDEX_FILE + elif test -f "$NEXT_INDEX" + then + GIT_INDEX_FILE="$NEXT_INDEX" + export GIT_INDEX_FILE + fi + + if test "$status_only" = "t" -o "$use_status_color" = "t"; then + color= + else + color=--nocolor + fi + git runstatus ${color} \ + ${verbose:+--verbose} \ + ${amend:+--amend} \ + ${untracked_files:+--untracked} +} + +trap ' + test -z "$TMP_INDEX" || { + test -f "$TMP_INDEX" && rm -f "$TMP_INDEX" + } + rm -f "$NEXT_INDEX" +' 0 + +################################################################ +# Command line argument parsing and sanity checking + +all= +also= +interactive= +only= +logfile= +use_commit= +amend= +edit_flag= +no_edit= +log_given= +log_message= +verify=t +quiet= +verbose= +signoff= +force_author= +only_include_assumed= +untracked_files= +templatefile="`git config commit.template`" +while test $# != 0 +do + case "$1" in + -F|--F|-f|--f|--fi|--fil|--file) + case "$#" in 1) usage ;; esac + shift + no_edit=t + log_given=t$log_given + logfile="$1" + ;; + -F*|-f*) + no_edit=t + log_given=t$log_given + logfile="${1#-[Ff]}" + ;; + --F=*|--f=*|--fi=*|--fil=*|--file=*) + no_edit=t + log_given=t$log_given + logfile="${1#*=}" + ;; + -a|--a|--al|--all) + all=t + ;; + --au=*|--aut=*|--auth=*|--autho=*|--author=*) + force_author="${1#*=}" + ;; + --au|--aut|--auth|--autho|--author) + case "$#" in 1) usage ;; esac + shift + force_author="$1" + ;; + -e|--e|--ed|--edi|--edit) + edit_flag=t + ;; + -i|--i|--in|--inc|--incl|--inclu|--includ|--include) + also=t + ;; + --int|--inte|--inter|--intera|--interac|--interact|--interacti|\ + --interactiv|--interactive) + interactive=t + ;; + -o|--o|--on|--onl|--only) + only=t + ;; + -m|--m|--me|--mes|--mess|--messa|--messag|--message) + case "$#" in 1) usage ;; esac + shift + log_given=m$log_given + log_message="${log_message:+${log_message} + +}$1" + no_edit=t + ;; + -m*) + log_given=m$log_given + log_message="${log_message:+${log_message} + +}${1#-m}" + no_edit=t + ;; + --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*) + log_given=m$log_given + log_message="${log_message:+${log_message} + +}${1#*=}" + no_edit=t + ;; + -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\ + --no-verify) + verify= + ;; + --a|--am|--ame|--amen|--amend) + amend=t + use_commit=HEAD + ;; + -c) + case "$#" in 1) usage ;; esac + shift + log_given=t$log_given + use_commit="$1" + no_edit= + ;; + --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\ + --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\ + --reedit-messag=*|--reedit-message=*) + log_given=t$log_given + use_commit="${1#*=}" + no_edit= + ;; + --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\ + --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\ + --reedit-message) + case "$#" in 1) usage ;; esac + shift + log_given=t$log_given + use_commit="$1" + no_edit= + ;; + -C) + case "$#" in 1) usage ;; esac + shift + log_given=t$log_given + use_commit="$1" + no_edit=t + ;; + --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\ + --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\ + --reuse-message=*) + log_given=t$log_given + use_commit="${1#*=}" + no_edit=t + ;; + --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\ + --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message) + case "$#" in 1) usage ;; esac + shift + log_given=t$log_given + use_commit="$1" + no_edit=t + ;; + -s|--s|--si|--sig|--sign|--signo|--signof|--signoff) + signoff=t + ;; + -t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template) + case "$#" in 1) usage ;; esac + shift + templatefile="$1" + no_edit= + ;; + -q|--q|--qu|--qui|--quie|--quiet) + quiet=t + ;; + -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) + verbose=t + ;; + -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\ + --untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\ + --untracked-file|--untracked-files) + untracked_files=t + ;; + --) + shift + break + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift +done +case "$edit_flag" in t) no_edit= ;; esac + +################################################################ +# Sanity check options + +case "$amend,$initial_commit" in +t,t) + die "You do not have anything to amend." ;; +t,) + if [ -f "$GIT_DIR/MERGE_HEAD" ]; then + die "You are in the middle of a merge -- cannot amend." + fi ;; +esac + +case "$log_given" in +tt*) + die "Only one of -c/-C/-F can be used." ;; +*tm*|*mt*) + die "Option -m cannot be combined with -c/-C/-F." ;; +esac + +case "$#,$also,$only,$amend" in +*,t,t,*) + die "Only one of --include/--only can be used." ;; +0,t,,* | 0,,t,) + die "No paths with --include/--only does not make sense." ;; +0,,t,t) + only_include_assumed="# Clever... amending the last one with dirty index." ;; +0,,,*) + ;; +*,,,*) + only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..." + also= + ;; +esac +unset only +case "$all,$interactive,$also,$#" in +*t,*t,*) + die "Cannot use -a, --interactive or -i at the same time." ;; +t,,,[1-9]*) + die "Paths with -a does not make sense." ;; +,t,,[1-9]*) + die "Paths with --interactive does not make sense." ;; +,,t,0) + die "No paths with -i does not make sense." ;; +esac + +if test ! -z "$templatefile" -a -z "$log_given" +then + if test ! -f "$templatefile" + then + die "Commit template file does not exist." + fi +fi + +################################################################ +# Prepare index to have a tree to be committed + +case "$all,$also" in +t,) + if test ! -f "$THIS_INDEX" + then + die 'nothing to commit (use "git add file1 file2" to include for commit)' + fi + save_index && + ( + cd_to_toplevel && + GIT_INDEX_FILE="$NEXT_INDEX" && + export GIT_INDEX_FILE && + git diff-files --name-only -z | + git update-index --remove -z --stdin + ) || exit + ;; +,t) + save_index && + git ls-files --error-unmatch -- "$@" >/dev/null || exit + + git diff-files --name-only -z -- "$@" | + ( + cd_to_toplevel && + GIT_INDEX_FILE="$NEXT_INDEX" && + export GIT_INDEX_FILE && + git update-index --remove -z --stdin + ) || exit + ;; +,) + if test "$interactive" = t; then + git add --interactive || exit + fi + case "$#" in + 0) + ;; # commit as-is + *) + if test -f "$GIT_DIR/MERGE_HEAD" + then + refuse_partial "Cannot do a partial commit during a merge." + fi + + TMP_INDEX="$GIT_DIR/tmp-index$$" + W= + test -z "$initial_commit" && W=--with-tree=HEAD + commit_only=`git ls-files --error-unmatch $W -- "$@"` || exit + + # Build a temporary index and update the real index + # the same way. + if test -z "$initial_commit" + then + GIT_INDEX_FILE="$THIS_INDEX" \ + git read-tree --index-output="$TMP_INDEX" -i -m HEAD + else + rm -f "$TMP_INDEX" + fi || exit + + printf '%s\n' "$commit_only" | + GIT_INDEX_FILE="$TMP_INDEX" \ + git update-index --add --remove --stdin && + + save_index && + printf '%s\n' "$commit_only" | + ( + GIT_INDEX_FILE="$NEXT_INDEX" + export GIT_INDEX_FILE + git update-index --add --remove --stdin + ) || exit + ;; + esac + ;; +esac + +################################################################ +# If we do as-is commit, the index file will be THIS_INDEX, +# otherwise NEXT_INDEX after we make this commit. We leave +# the index as is if we abort. + +if test -f "$NEXT_INDEX" +then + USE_INDEX="$NEXT_INDEX" +else + USE_INDEX="$THIS_INDEX" +fi + +case "$status_only" in +t) + # This will silently fail in a read-only repository, which is + # what we want. + GIT_INDEX_FILE="$USE_INDEX" git update-index -q --unmerged --refresh + run_status + exit $? + ;; +'') + GIT_INDEX_FILE="$USE_INDEX" git update-index -q --refresh || exit + ;; +esac + +################################################################ +# Grab commit message, write out tree and make commit. + +if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit +then + GIT_INDEX_FILE="${TMP_INDEX:-${USE_INDEX}}" "$GIT_DIR"/hooks/pre-commit \ + || exit +fi + +if test "$log_message" != '' +then + printf '%s\n' "$log_message" +elif test "$logfile" != "" +then + if test "$logfile" = - + then + test -t 0 && + echo >&2 "(reading log message from standard input)" + cat + else + cat <"$logfile" + fi +elif test "$use_commit" != "" +then + encoding=$(git config i18n.commitencoding || echo UTF-8) + git show -s --pretty=raw --encoding="$encoding" "$use_commit" | + sed -e '1,/^$/d' -e 's/^ //' +elif test -f "$GIT_DIR/MERGE_MSG" +then + cat "$GIT_DIR/MERGE_MSG" +elif test -f "$GIT_DIR/SQUASH_MSG" +then + cat "$GIT_DIR/SQUASH_MSG" +elif test "$templatefile" != "" +then + cat "$templatefile" +fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG + +case "$signoff" in +t) + sign=$(git-var GIT_COMMITTER_IDENT | sed -e ' + s/>.*/>/ + s/^/Signed-off-by: / + ') + blank_before_signoff= + tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG | + grep 'Signed-off-by:' >/dev/null || blank_before_signoff=' +' + tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG | + grep "$sign"$ >/dev/null || + printf '%s%s\n' "$blank_before_signoff" "$sign" \ + >>"$GIT_DIR"/COMMIT_EDITMSG + ;; +esac + +if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then + echo "#" + echo "# It looks like you may be committing a MERGE." + echo "# If this is not correct, please remove the file" + printf '%s\n' "# $GIT_DIR/MERGE_HEAD" + echo "# and try again" + echo "#" +fi >>"$GIT_DIR"/COMMIT_EDITMSG + +# Author +if test '' != "$use_commit" +then + eval "$(get_author_ident_from_commit "$use_commit")" + export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE +fi +if test '' != "$force_author" +then + GIT_AUTHOR_NAME=`expr "z$force_author" : 'z\(.*[^ ]\) *<.*'` && + GIT_AUTHOR_EMAIL=`expr "z$force_author" : '.*\(<.*\)'` && + test '' != "$GIT_AUTHOR_NAME" && + test '' != "$GIT_AUTHOR_EMAIL" || + die "malformed --author parameter" + export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL +fi + +PARENTS="-p HEAD" +if test -z "$initial_commit" +then + rloga='commit' + if [ -f "$GIT_DIR/MERGE_HEAD" ]; then + rloga='commit (merge)' + PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"` + elif test -n "$amend"; then + rloga='commit (amend)' + PARENTS=$(git cat-file commit HEAD | + sed -n -e '/^$/q' -e 's/^parent /-p /p') + fi + current="$(git rev-parse --verify HEAD)" +else + if [ -z "$(git ls-files)" ]; then + echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)' + exit 1 + fi + PARENTS="" + rloga='commit (initial)' + current='' +fi +set_reflog_action "$rloga" + +if test -z "$no_edit" +then + { + echo "" + echo "# Please enter the commit message for your changes." + echo "# (Comment lines starting with '#' will not be included)" + test -z "$only_include_assumed" || echo "$only_include_assumed" + run_status + } >>"$GIT_DIR"/COMMIT_EDITMSG +else + # we need to check if there is anything to commit + run_status >/dev/null +fi +if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ] +then + rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG" + use_status_color=t + run_status + exit 1 +fi + +case "$no_edit" in +'') + git-var GIT_AUTHOR_IDENT > /dev/null || die + git-var GIT_COMMITTER_IDENT > /dev/null || die + git_editor "$GIT_DIR/COMMIT_EDITMSG" + ;; +esac + +case "$verify" in +t) + if test -x "$GIT_DIR"/hooks/commit-msg + then + "$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit + fi +esac + +if test -z "$no_edit" +then + sed -e ' + /^diff --git a\/.*/{ + s/// + q + } + /^#/d + ' "$GIT_DIR"/COMMIT_EDITMSG +else + cat "$GIT_DIR"/COMMIT_EDITMSG +fi | +git stripspace >"$GIT_DIR"/COMMIT_MSG + +# Test whether the commit message has any content we didn't supply. +have_commitmsg= +grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG | + git stripspace > "$GIT_DIR"/COMMIT_BAREMSG + +# Is the commit message totally empty? +if test -s "$GIT_DIR"/COMMIT_BAREMSG +then + if test "$templatefile" != "" + then + # Test whether this is just the unaltered template. + if cnt=`sed -e '/^#/d' < "$templatefile" | + git stripspace | + diff "$GIT_DIR"/COMMIT_BAREMSG - | + wc -l` && + test 0 -lt $cnt + then + have_commitmsg=t + fi + else + # No template, so the content in the commit message must + # have come from the user. + have_commitmsg=t + fi +fi + +rm -f "$GIT_DIR"/COMMIT_BAREMSG + +if test "$have_commitmsg" = "t" +then + if test -z "$TMP_INDEX" + then + tree=$(GIT_INDEX_FILE="$USE_INDEX" git write-tree) + else + tree=$(GIT_INDEX_FILE="$TMP_INDEX" git write-tree) && + rm -f "$TMP_INDEX" + fi && + commit=$(git commit-tree $tree $PARENTS <"$GIT_DIR/COMMIT_MSG") && + rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) && + git update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" && + rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" && + if test -f "$NEXT_INDEX" + then + mv "$NEXT_INDEX" "$THIS_INDEX" + else + : ;# happy + fi +else + echo >&2 "* no commit message? aborting commit." + false +fi +ret="$?" +rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG" + +cd_to_toplevel + +git rerere + +if test "$ret" = 0 +then + git gc --auto + if test -x "$GIT_DIR"/hooks/post-commit + then + "$GIT_DIR"/hooks/post-commit + fi + if test -z "$quiet" + then + commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\ + --summary --root HEAD --` + echo "Created${initial_commit:+ initial} commit $commit" + fi +fi + +exit "$ret" -- cgit v1.2.3 From b3a4f8586b4ffa6c896cf0afb2ea49d64faf81ad Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 23 Nov 2007 01:11:35 +0000 Subject: bash completion: add diff options I use "git diff" (the porcelain) really often, and am almost as often annoyed that the completions do not know how to complete something simple as --cached. Now they do. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 599b2fc571..58e0e53cd6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -551,6 +551,20 @@ _git_describe () _git_diff () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --stat --numstat --shortstat --summary + --patch-with-stat --name-only --name-status --color + --no-color --color-words --no-renames --check + --full-index --binary --abbrev --diff-filter + --find-copies-harder --pickaxe-all --pickaxe-regex + --text --ignore-space-at-eol --ignore-space-change + --ignore-all-space --exit-code --quiet --ext-diff + --no-ext-diff" + return + ;; + esac __git_complete_file } -- cgit v1.2.3 From afa75bc8aa1d453d18cc2486ba8fc53e7df92c4d Mon Sep 17 00:00:00 2001 From: Jakub Narebski Date: Sun, 2 Dec 2007 20:40:43 +0100 Subject: contrib: Make remotes2config.sh script more robust The remotes2config.sh script replaced all 'unsafe' characters in repo name with '.'; include '-' in the 'safe' characters set (the set is probably even larger). Script required also space after "URL:", "Push:" and "Pull:" in remotes file. This for example made the following remote URL: git://git.kernel.org/pub/scm/git/git.git Pull: refs/heads/master:refs/heads/origin Pull:+refs/heads/pu:refs/heads/pu miss 'pu' branch (forced branch) in config file after conversion. Allow for any number of whitespace after "URL:", "Push:", "Pull:". Signed-off-by: Jakub Narebski Signed-off-by: Junio C Hamano --- contrib/remotes2config.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) mode change 100644 => 100755 contrib/remotes2config.sh (limited to 'contrib') diff --git a/contrib/remotes2config.sh b/contrib/remotes2config.sh old mode 100644 new mode 100755 index 5838b3ab05..1cda19f66a --- a/contrib/remotes2config.sh +++ b/contrib/remotes2config.sh @@ -11,11 +11,11 @@ if [ -d "$GIT_DIR"/remotes ]; then { cd "$GIT_DIR"/remotes ls | while read f; do - name=$(printf "$f" | tr -c "A-Za-z0-9" ".") + name=$(printf "$f" | tr -c "A-Za-z0-9-" ".") sed -n \ - -e "s/^URL: \(.*\)$/remote.$name.url \1 ./p" \ - -e "s/^Pull: \(.*\)$/remote.$name.fetch \1 ^$ /p" \ - -e "s/^Push: \(.*\)$/remote.$name.push \1 ^$ /p" \ + -e "s/^URL:[ ]*\(.*\)$/remote.$name.url \1 ./p" \ + -e "s/^Pull:[ ]*\(.*\)$/remote.$name.fetch \1 ^$ /p" \ + -e "s/^Push:[ ]*\(.*\)$/remote.$name.push \1 ^$ /p" \ < "$f" done echo done -- cgit v1.2.3 From 90e0653b1824f27559cbc5c9d1f2a00fdb9400ba Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 6 Dec 2007 07:26:29 -0800 Subject: hg-to-git: handle an empty dir in hg. Mark Drago had a subversion repository which was then converted to hg and now is moving in to git. The first commit in the svn repo was just the creation of the empty directory. This made its way in to the hg repository fine, but converting from hg to git would cause an error. The problem was that hg-to-git.py tries to commit the change, git-commit fails, and then hg-to-git.py tries to checkout the new revision and that fails (because it was not created). This may have only caused an error because it was the first commit in the repository. If an empty directory was added in the middle of the repo somewhere things might have worked out fine. This patch will use the new --allow-empty option to git-commit to record such an "empty" commit, to reproduce the history recorded in hg more faithfully. Tested-by: Mark Drago Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 7a1c3e497f..9befb92c41 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -211,7 +211,7 @@ for cset in range(int(tip) + 1): os.system('git-ls-files -x .hg --deleted | git-update-index --remove --stdin') # commit - os.system(getgitenv(user, date) + 'git-commit -a -F %s' % filecomment) + os.system(getgitenv(user, date) + 'git commit --allow-empty -a -F %s' % filecomment) os.unlink(filecomment) # tag -- cgit v1.2.3 From 18ff365fcb3b16d6802c04da03ceda3a260c0ab9 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 11 Dec 2007 13:56:09 +0100 Subject: git.el: Added a menu for git-status-mode. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Originally written by Rémi Vanicat, I just changed the layout a little. Signed-off-by: Rémi Vanicat Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index e147da0596..28a4899a0a 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -49,6 +49,7 @@ (eval-when-compile (require 'cl)) (require 'ewoc) (require 'log-edit) +(require 'easymenu) ;;;; Customizations @@ -1297,7 +1298,47 @@ Return the list of files that haven't been handled." (define-key toggle-map "i" 'git-toggle-show-ignored) (define-key toggle-map "k" 'git-toggle-show-unknown) (define-key toggle-map "m" 'git-toggle-all-marks) - (setq git-status-mode-map map))) + (setq git-status-mode-map map)) + (easy-menu-define git-menu git-status-mode-map + "Git Menu" + `("Git" + ["Refresh" git-refresh-status t] + ["Commit" git-commit-file t] + ("Merge" + ["Next Unmerged File" git-next-unmerged-file t] + ["Prev Unmerged File" git-prev-unmerged-file t] + ["Mark as Resolved" git-resolve-file t] + ["Interactive Merge File" git-find-file-imerge t] + ["Diff Against Common Base File" git-diff-file-base t] + ["Diff Combined" git-diff-file-combined t] + ["Diff Against Merge Head" git-diff-file-merge-head t] + ["Diff Against Mine" git-diff-file-mine t] + ["Diff Against Other" git-diff-file-other t]) + "--------" + ["Add File" git-add-file t] + ["Revert File" git-revert-file t] + ["Ignore File" git-ignore-file t] + ["Remove File" git-remove-file t] + "--------" + ["Find File" git-find-file t] + ["View File" git-view-file t] + ["Diff File" git-diff-file t] + ["Interactive Diff File" git-diff-file-idiff t] + ["Log" git-log-file t] + "--------" + ["Mark" git-mark-file t] + ["Mark All" git-mark-all t] + ["Unmark" git-unmark-file t] + ["Unmark All" git-unmark-all t] + ["Toggle All Marks" git-toggle-all-marks t] + ["Hide Handled Files" git-remove-handled t] + "--------" + ["Show Uptodate Files" git-toggle-show-uptodate :style toggle :selected git-show-uptodate] + ["Show Ignored Files" git-toggle-show-ignored :style toggle :selected git-show-ignored] + ["Show Unknown Files" git-toggle-show-unknown :style toggle :selected git-show-unknown] + "--------" + ["Quit" git-status-quit t]))) + ;; git mode should only run in the *git status* buffer (put 'git-status-mode 'mode-class 'special) -- cgit v1.2.3 From 718a087a47cc148f74027a3a26d71994ff71bdd8 Mon Sep 17 00:00:00 2001 From: Jeff King Date: Sat, 15 Dec 2007 06:11:54 -0500 Subject: teach bash completion to treat commands with "--" as a helper There is a convention that commands containing a double-dash are implementation details and not to be used by mortals. We should automatically remove them from the completion suggestions as such. Signed-off-by: Jeff King Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 58e0e53cd6..343364de04 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -291,7 +291,7 @@ __git_commands () for i in $(git help -a|egrep '^ ') do case $i in - add--interactive) : plumbing;; + *--*) : helper pattern;; applymbox) : ask gittus;; applypatch) : ask gittus;; archimport) : import;; @@ -308,7 +308,6 @@ __git_commands () diff-tree) : plumbing;; fast-import) : import;; fsck-objects) : plumbing;; - fetch--tool) : plumbing;; fetch-pack) : plumbing;; fmt-merge-msg) : plumbing;; for-each-ref) : plumbing;; -- cgit v1.2.3 From e25cfae6677423d05a81c0862f58d3319141794c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 26 Dec 2007 17:38:00 -0800 Subject: contrib: resurrect scripted git-revert. Signed-off-by: Junio C Hamano --- contrib/examples/git-revert.sh | 197 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100755 contrib/examples/git-revert.sh (limited to 'contrib') diff --git a/contrib/examples/git-revert.sh b/contrib/examples/git-revert.sh new file mode 100755 index 0000000000..49f00321b2 --- /dev/null +++ b/contrib/examples/git-revert.sh @@ -0,0 +1,197 @@ +#!/bin/sh +# +# Copyright (c) 2005 Linus Torvalds +# Copyright (c) 2005 Junio C Hamano +# + +case "$0" in +*-revert* ) + test -t 0 && edit=-e + replay= + me=revert + USAGE='[--edit | --no-edit] [-n] ' ;; +*-cherry-pick* ) + replay=t + edit= + me=cherry-pick + USAGE='[--edit] [-n] [-r] [-x] ' ;; +* ) + echo >&2 "What are you talking about?" + exit 1 ;; +esac + +SUBDIRECTORY_OK=Yes ;# we will cd up +. git-sh-setup +require_work_tree +cd_to_toplevel + +no_commit= +while case "$#" in 0) break ;; esac +do + case "$1" in + -n|--n|--no|--no-|--no-c|--no-co|--no-com|--no-comm|\ + --no-commi|--no-commit) + no_commit=t + ;; + -e|--e|--ed|--edi|--edit) + edit=-e + ;; + --n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit) + edit= + ;; + -r) + : no-op ;; + -x|--i-really-want-to-expose-my-private-commit-object-name) + replay= + ;; + -*) + usage + ;; + *) + break + ;; + esac + shift +done + +set_reflog_action "$me" + +test "$me,$replay" = "revert,t" && usage + +case "$no_commit" in +t) + # We do not intend to commit immediately. We just want to + # merge the differences in. + head=$(git-write-tree) || + die "Your index file is unmerged." + ;; +*) + head=$(git-rev-parse --verify HEAD) || + die "You do not have a valid HEAD" + files=$(git-diff-index --cached --name-only $head) || exit + if [ "$files" ]; then + die "Dirty index: cannot $me (dirty: $files)" + fi + ;; +esac + +rev=$(git-rev-parse --verify "$@") && +commit=$(git-rev-parse --verify "$rev^0") || + die "Not a single commit $@" +prev=$(git-rev-parse --verify "$commit^1" 2>/dev/null) || + die "Cannot run $me a root commit" +git-rev-parse --verify "$commit^2" >/dev/null 2>&1 && + die "Cannot run $me a multi-parent commit." + +encoding=$(git config i18n.commitencoding || echo UTF-8) + +# "commit" is an existing commit. We would want to apply +# the difference it introduces since its first parent "prev" +# on top of the current HEAD if we are cherry-pick. Or the +# reverse of it if we are revert. + +case "$me" in +revert) + git show -s --pretty=oneline --encoding="$encoding" $commit | + sed -e ' + s/^[^ ]* /Revert "/ + s/$/"/ + ' + echo + echo "This reverts commit $commit." + test "$rev" = "$commit" || + echo "(original 'git revert' arguments: $@)" + base=$commit next=$prev + ;; + +cherry-pick) + pick_author_script=' + /^author /{ + s/'\''/'\''\\'\'\''/g + h + s/^author \([^<]*\) <[^>]*> .*$/\1/ + s/'\''/'\''\'\'\''/g + s/.*/GIT_AUTHOR_NAME='\''&'\''/p + + g + s/^author [^<]* <\([^>]*\)> .*$/\1/ + s/'\''/'\''\'\'\''/g + s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p + + g + s/^author [^<]* <[^>]*> \(.*\)$/\1/ + s/'\''/'\''\'\'\''/g + s/.*/GIT_AUTHOR_DATE='\''&'\''/p + + q + }' + + logmsg=`git show -s --pretty=raw --encoding="$encoding" "$commit"` + set_author_env=`echo "$logmsg" | + LANG=C LC_ALL=C sed -ne "$pick_author_script"` + eval "$set_author_env" + export GIT_AUTHOR_NAME + export GIT_AUTHOR_EMAIL + export GIT_AUTHOR_DATE + + echo "$logmsg" | + sed -e '1,/^$/d' -e 's/^ //' + case "$replay" in + '') + echo "(cherry picked from commit $commit)" + test "$rev" = "$commit" || + echo "(original 'git cherry-pick' arguments: $@)" + ;; + esac + base=$prev next=$commit + ;; + +esac >.msg + +eval GITHEAD_$head=HEAD +eval GITHEAD_$next='`git show -s \ + --pretty=oneline --encoding="$encoding" "$commit" | + sed -e "s/^[^ ]* //"`' +export GITHEAD_$head GITHEAD_$next + +# This three way merge is an interesting one. We are at +# $head, and would want to apply the change between $commit +# and $prev on top of us (when reverting), or the change between +# $prev and $commit on top of us (when cherry-picking or replaying). + +git-merge-recursive $base -- $head $next && +result=$(git-write-tree 2>/dev/null) || { + mv -f .msg "$GIT_DIR/MERGE_MSG" + { + echo ' +Conflicts: +' + git ls-files --unmerged | + sed -e 's/^[^ ]* / /' | + uniq + } >>"$GIT_DIR/MERGE_MSG" + echo >&2 "Automatic $me failed. After resolving the conflicts," + echo >&2 "mark the corrected paths with 'git-add '" + echo >&2 "and commit the result." + case "$me" in + cherry-pick) + echo >&2 "You may choose to use the following when making" + echo >&2 "the commit:" + echo >&2 "$set_author_env" + esac + exit 1 +} +echo >&2 "Finished one $me." + +# If we are cherry-pick, and if the merge did not result in +# hand-editing, we will hit this commit and inherit the original +# author date and name. +# If we are revert, or if our cherry-pick results in a hand merge, +# we had better say that the current user is responsible for that. + +case "$no_commit" in +'') + git-commit -n -F .msg $edit + rm -f .msg + ;; +esac -- cgit v1.2.3 From 8b30aa50593b159791fe1478cb5725caaf219d06 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 6 Jan 2008 12:12:24 +0100 Subject: git.el: Support for getting diffs from inside the log-edit buffer. Take advantage of the new log-edit feature that allows to show a diff with C-c C-d while editing the log message. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 28a4899a0a..cadb992336 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1024,7 +1024,9 @@ Return the list of files that haven't been handled." (setq default-directory dir) (setq buffer-read-only t))) (display-buffer buffer) - (shrink-window-if-larger-than-buffer)) + ; shrink window only if it displays the status buffer + (when (eq (window-buffer) (current-buffer)) + (shrink-window-if-larger-than-buffer))) (defun git-diff-file () "Diff the marked file(s) against HEAD." @@ -1097,6 +1099,11 @@ Return the list of files that haven't been handled." (with-current-buffer log-edit-parent-buffer (git-get-filenames (git-marked-files-state 'added 'deleted 'modified)))) +(defun git-log-edit-diff () + "Run a diff of the current files being committed from a log-edit buffer." + (with-current-buffer log-edit-parent-buffer + (git-diff-file))) + (defun git-append-sign-off (name email) "Append a Signed-off-by entry to the current buffer, avoiding duplicates." (let ((sign-off (format "Signed-off-by: %s <%s>" name email)) @@ -1169,7 +1176,10 @@ Return the list of files that haven't been handled." (when (re-search-forward "^Date: \\(.*\\)$" nil t) (setq date (match-string 1))))) (git-setup-log-buffer buffer author-name author-email subject date)) - (log-edit #'git-do-commit nil #'git-log-edit-files buffer) + (if (boundp 'log-edit-diff-function) + (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files) + (log-edit-diff-function . git-log-edit-diff)) buffer) + (log-edit 'git-do-commit nil 'git-log-edit-files buffer)) (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) (setq buffer-file-coding-system coding-system) (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) -- cgit v1.2.3 From 5e3cb7e5038f99d442c484d5839bd6bf02adb58c Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 6 Jan 2008 12:13:01 +0100 Subject: git.el: Retrieve the permissions for up-to-date files. This allows displaying correctly the executable flag for the initial commit, and will make it possible to show the file type for up-to-date symlinks and subprojects. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index cadb992336..df3699b3a6 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -642,6 +642,22 @@ Return the list of files that haven't been handled." (git-insert-info-list status infolist) files)) +(defun git-run-ls-files-cached (status files default-state) + "Run git-ls-files -c on FILES and parse the results into STATUS. +Return the list of files that haven't been handled." + (let (infolist) + (with-temp-buffer + (apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files) + (goto-char (point-min)) + (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-3]\t\\([^\0]+\\)\0" nil t) + (let* ((new-perm (string-to-number (match-string 1) 8)) + (old-perm (if (eq default-state 'added) 0 new-perm)) + (name (match-string 2))) + (push (git-create-fileinfo default-state name old-perm new-perm) infolist) + (setq files (delete name files))))) + (git-insert-info-list status infolist) + files)) + (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." (with-temp-buffer @@ -673,10 +689,10 @@ Return the list of files that haven't been handled." "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) (unless files - (when git-show-uptodate (git-run-ls-files git-status nil 'uptodate "-c"))) + (when git-show-uptodate (git-run-ls-files-cached git-status nil 'uptodate))) (let* ((remaining-files (if (git-empty-db-p) ; we need some special handling for an empty db - (git-run-ls-files git-status files 'added "-c") + (git-run-ls-files-cached git-status files 'added) (git-run-diff-index git-status files)))) (git-run-ls-unmerged git-status files) (when (or remaining-files (and git-show-unknown (not files))) -- cgit v1.2.3 From 40f162b04b3d5155a7e41d27c9f52383a5fa5a9f Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 6 Jan 2008 12:13:36 +0100 Subject: git.el: Display file types and type changes. Handle the T status from git-diff-index to display type changes between file/symlink/subproject. Also always show the file type for symlink and subprojects to indicate that they are not normal files. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index df3699b3a6..9a0f03f242 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -489,8 +489,7 @@ and returns the process output as a string." "Set the state of a file info." (unless (eq (git-fileinfo->state info) state) (setf (git-fileinfo->state info) state - (git-fileinfo->old-perm info) 0 - (git-fileinfo->new-perm info) 0 + (git-fileinfo->new-perm info) (git-fileinfo->old-perm info) (git-fileinfo->rename-state info) nil (git-fileinfo->orig-name info) nil (git-fileinfo->needs-refresh info) t))) @@ -524,6 +523,7 @@ and returns the process output as a string." (?A 'added) (?D 'deleted) (?U 'unmerged) + (?T 'modified) (t nil))) (defun git-status-code-as-string (code) @@ -538,6 +538,33 @@ and returns the process output as a string." ('ignored (propertize "Ignored " 'face 'git-ignored-face)) (t "? "))) +(defun git-file-type-as-string (info) + "Return a string describing the file type of INFO." + (let* ((old-type (lsh (or (git-fileinfo->old-perm info) 0) -9)) + (new-type (lsh (or (git-fileinfo->new-perm info) 0) -9)) + (str (case new-type + (?\100 ;; file + (case old-type + (?\100 nil) + (?\120 " (type change symlink -> file)") + (?\160 " (type change subproject -> file)"))) + (?\120 ;; symlink + (case old-type + (?\100 " (type change file -> symlink)") + (?\160 " (type change subproject -> symlink)") + (t " (symlink)"))) + (?\160 ;; subproject + (case old-type + (?\100 " (type change file -> subproject)") + (?\120 " (type change symlink -> subproject)") + (t " (subproject)"))) + (?\000 ;; deleted or unknown + (case old-type + (?\120 " (symlink)") + (?\160 " (subproject)"))) + (t (format " (unknown type %o)" new-type))))) + (if str (propertize str 'face 'git-status-face) ""))) + (defun git-rename-as-string (info) "Return a string describing the copy or rename associated with INFO, or an empty string if none." (let ((state (git-fileinfo->rename-state info))) @@ -567,6 +594,7 @@ and returns the process output as a string." " " (git-status-code-as-string (git-fileinfo->state info)) " " (git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info)) " " (git-escape-file-name (git-fileinfo->name info)) + (git-file-type-as-string info) (git-rename-as-string info)))) (defun git-insert-info-list (status infolist) @@ -603,7 +631,7 @@ Return the list of files that haven't been handled." (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) (while (re-search-forward - ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" + ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" nil t 1) (let ((old-perm (string-to-number (match-string 1) 8)) (new-perm (string-to-number (match-string 2) 8)) -- cgit v1.2.3 From 87e3d812943f3d9c5f9464b0aee83398dd9d028e Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 8 Jan 2008 14:45:46 +0100 Subject: git.el: Make sure we never insert the same file twice. Skip non-zero stage files during git-ls-files -c, they are handled later. Also fix git-insert-info-list to merge duplicate file names. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 9a0f03f242..c826017580 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -608,7 +608,7 @@ and returns the process output as a string." (while info (setf (git-fileinfo->needs-refresh info) t) (cond ((not node) - (ewoc-enter-last status info) + (setq node (ewoc-enter-last status info)) (setq info (pop infolist))) ((string-lessp (git-fileinfo->name (ewoc-data node)) (git-fileinfo->name info)) @@ -620,7 +620,7 @@ and returns the process output as a string." (setf (ewoc-data node) info) (setq info (pop infolist))) (t - (ewoc-enter-before status node info) + (setq node (ewoc-enter-before status node info)) (setq info (pop infolist))))))) (defun git-run-diff-index (status files) @@ -677,7 +677,7 @@ Return the list of files that haven't been handled." (with-temp-buffer (apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files) (goto-char (point-min)) - (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-3]\t\\([^\0]+\\)\0" nil t) + (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t) (let* ((new-perm (string-to-number (match-string 1) 8)) (old-perm (if (eq default-state 'added) 0 new-perm)) (name (match-string 2))) -- cgit v1.2.3 From 58152a02d99b499c338b633e7fe021b337313bd8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 8 Jan 2008 14:46:22 +0100 Subject: git.el: Refresh files from their real state upon commit. Instead of just setting the state to up-to-date, retrieve the full state again, so that the file type can be displayed properly. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index c826017580..342c78348e 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -626,7 +626,8 @@ and returns the process output as a string." (defun git-run-diff-index (status files) "Run git-diff-index on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let (infolist) + (let ((remaining (copy-sequence files)) + infolist) (with-temp-buffer (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) @@ -644,10 +645,10 @@ Return the list of files that haven't been handled." (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist) (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist)) (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist)) - (setq files (delete name files)) - (when new-name (setq files (delete new-name files)))))) + (setq remaining (delete name remaining)) + (when new-name (setq remaining (delete new-name remaining)))))) (git-insert-info-list status infolist) - files)) + remaining)) (defun git-find-status-file (status file) "Find a given file in the status ewoc and return its node." @@ -673,7 +674,8 @@ Return the list of files that haven't been handled." (defun git-run-ls-files-cached (status files default-state) "Run git-ls-files -c on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let (infolist) + (let ((remaining (copy-sequence files)) + infolist) (with-temp-buffer (apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files) (goto-char (point-min)) @@ -682,9 +684,9 @@ Return the list of files that haven't been handled." (old-perm (if (eq default-state 'added) 0 new-perm)) (name (match-string 2))) (push (git-create-fileinfo default-state name old-perm new-perm) infolist) - (setq files (delete name files))))) + (setq remaining (delete name remaining))))) (git-insert-info-list status infolist) - files)) + remaining)) (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." @@ -716,8 +718,8 @@ Return the list of files that haven't been handled." (defun git-update-status-files (files &optional default-state) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) - (unless files - (when git-show-uptodate (git-run-ls-files-cached git-status nil 'uptodate))) + (when (or git-show-uptodate files) + (git-run-ls-files-cached git-status files 'uptodate)) (let* ((remaining-files (if (git-empty-db-p) ; we need some special handling for an empty db (git-run-ls-files-cached git-status files 'added) @@ -839,7 +841,7 @@ Return the list of files that haven't been handled." (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)) - (dolist (info files) (git-set-fileinfo-state info 'uptodate)) + (git-update-status-files (git-get-filenames files) 'uptodate) (git-call-process-env nil nil "rerere") (git-call-process-env nil nil "gc" "--auto") (git-refresh-files) -- cgit v1.2.3 From ef40b3efe0764f7b56c2745601690e9cc12428d8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 8 Jan 2008 14:49:09 +0100 Subject: git.el: Make status refresh faster. Don't set the needs-refresh flag when inserting a new file info, since ewoc refreshes it upon insert already; this makes a full refresh twice as fast. Also make git-fileinfo-prettyprint a little faster by not retrieving permission values twice. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 342c78348e..d8a06381f4 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -538,10 +538,10 @@ and returns the process output as a string." ('ignored (propertize "Ignored " 'face 'git-ignored-face)) (t "? "))) -(defun git-file-type-as-string (info) - "Return a string describing the file type of INFO." - (let* ((old-type (lsh (or (git-fileinfo->old-perm info) 0) -9)) - (new-type (lsh (or (git-fileinfo->new-perm info) 0) -9)) +(defun git-file-type-as-string (old-perm new-perm) + "Return a string describing the file type based on its permissions." + (let* ((old-type (lsh (or old-perm 0) -9)) + (new-type (lsh (or new-perm 0) -9)) (str (case new-type (?\100 ;; file (case old-type @@ -590,12 +590,14 @@ and returns the process output as a string." (defun git-fileinfo-prettyprint (info) "Pretty-printer for the git-fileinfo structure." - (insert (concat " " (if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ") - " " (git-status-code-as-string (git-fileinfo->state info)) - " " (git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info)) - " " (git-escape-file-name (git-fileinfo->name info)) - (git-file-type-as-string info) - (git-rename-as-string info)))) + (let ((old-perm (git-fileinfo->old-perm info)) + (new-perm (git-fileinfo->new-perm info))) + (insert (concat " " (if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ") + " " (git-status-code-as-string (git-fileinfo->state info)) + " " (git-permissions-as-string old-perm new-perm) + " " (git-escape-file-name (git-fileinfo->name info)) + (git-file-type-as-string old-perm new-perm) + (git-rename-as-string info))))) (defun git-insert-info-list (status infolist) "Insert a list of file infos in the status buffer, replacing existing ones if any." @@ -606,7 +608,6 @@ and returns the process output as a string." (let ((info (pop infolist)) (node (ewoc-nth status 0))) (while info - (setf (git-fileinfo->needs-refresh info) t) (cond ((not node) (setq node (ewoc-enter-last status info)) (setq info (pop infolist))) @@ -617,6 +618,7 @@ and returns the process output as a string." (git-fileinfo->name info)) ;; preserve the marked flag (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))) + (setf (git-fileinfo->needs-refresh info) t) (setf (ewoc-data node) info) (setq info (pop infolist))) (t -- cgit v1.2.3 From 22fa97d4e011de82b44fdc83c27e919a996ce1be Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 13 Jan 2008 22:51:01 -0600 Subject: Remove usage of git- (dash) commands from email hook Switch all git command calls to use the git (space) command format, and remove the use of git-repo-config in place of git config. Signed-off-by: Dan McGee Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 7511ea0797..77c88ebf1f 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -248,24 +248,24 @@ generate_update_branch_email() # In this case we want to issue an email containing only revisions # 3, 4, and N. Given (almost) by # - # git-rev-list N ^O --not --all + # git rev-list N ^O --not --all # # The reason for the "almost", is that the "--not --all" will take # precedence over the "N", and effectively will translate to # - # git-rev-list N ^O ^X ^N + # git rev-list N ^O ^X ^N # - # So, we need to build up the list more carefully. git-rev-parse - # will generate a list of revs that may be fed into git-rev-list. + # So, we need to build up the list more carefully. git rev-parse + # will generate a list of revs that may be fed into git rev-list. # We can get it to make the "--not --all" part and then filter out # the "^N" with: # - # git-rev-parse --not --all | grep -v N + # git rev-parse --not --all | grep -v N # - # Then, using the --stdin switch to git-rev-list we have effectively + # Then, using the --stdin switch to git rev-list we have effectively # manufactured # - # git-rev-list N ^O ^X + # git rev-list N ^O ^X # # This leaves a problem when someone else updates the repository # while this script is running. Their new value of the ref we're @@ -274,10 +274,10 @@ generate_update_branch_email() # all of our commits. What we really want is to exclude the current # value of $refname from the --not list, rather than N itself. So: # - # git-rev-parse --not --all | grep -v $(git-rev-parse $refname) + # git rev-parse --not --all | grep -v $(git rev-parse $refname) # # Get's us to something pretty safe (apart from the small time - # between refname being read, and git-rev-parse running - for that, + # between refname being read, and git rev-parse running - for that, # I give up) # # @@ -295,7 +295,7 @@ generate_update_branch_email() # As above, we need to take into account the presence of X; if # another branch is already in the repository and points at some of # the revisions that we are about to output - we don't want them. - # The solution is as before: git-rev-parse output filtered. + # The solution is as before: git rev-parse output filtered. # # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N # @@ -305,7 +305,7 @@ generate_update_branch_email() # for a branch update. Therefore we still want to output revisions # that have been output on a tag email. # - # Luckily, git-rev-parse includes just the tool. Instead of using + # Luckily, git rev-parse includes just the tool. Instead of using # "--all" we use "--branches"; this has the added benefit that # "remotes/" will be ignored as well. @@ -454,7 +454,7 @@ generate_update_atag_email() # generate_atag_email() { - # Use git-for-each-ref to pull out the individual fields from the + # Use git for-each-ref to pull out the individual fields from the # tag eval $(git for-each-ref --shell --format=' tagobject=%(*objectname) @@ -572,7 +572,7 @@ generate_general_email() else # What can we do here? The tag marks an object that is not # a commit, so there is no log for us to display. It's - # probably not wise to output git-cat-file as it could be a + # probably not wise to output git cat-file as it could be a # binary blob. We'll just say how big it is echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long." fi @@ -622,10 +622,10 @@ then projectdesc="UNNAMED PROJECT" fi -recipients=$(git repo-config hooks.mailinglist) -announcerecipients=$(git repo-config hooks.announcelist) -envelopesender=$(git-repo-config hooks.envelopesender) -emailprefix=$(git-repo-config hooks.emailprefix || echo '[SCM] ') +recipients=$(git config hooks.mailinglist) +announcerecipients=$(git config hooks.announcelist) +envelopesender=$(git config hooks.envelopesender) +emailprefix=$(git config hooks.emailprefix || echo '[SCM] ') # --- Main loop # Allow dual mode: run from the command line just like the update hook, or -- cgit v1.2.3 From 1bc7c13af9f936aa80893100120b542338a10bf4 Mon Sep 17 00:00:00 2001 From: Mark Drago Date: Mon, 14 Jan 2008 20:11:19 -0500 Subject: hg-to-git: improve popen calls This patch improves all of the popen calls in hg-to-git.py by specifying the template 'hg log' should use instead of calling 'hg log' and grepping for the desired data. Signed-off-by: Mark Drago Acked-by: Stelian Pop Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 46 ++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 24 deletions(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 9befb92c41..c35b15860d 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -1,6 +1,6 @@ #! /usr/bin/python -""" hg-to-svn.py - A Mercurial to GIT converter +""" hg-to-git.py - A Mercurial to GIT converter Copyright (C)2007 Stelian Pop @@ -27,6 +27,8 @@ import re hgvers = {} # List of children for each hg revision hgchildren = {} +# List of parents for each hg revision +hgparents = {} # Current branch for each hg revision hgbranch = {} # Number of new changesets converted from hg @@ -99,17 +101,19 @@ if state: else: print 'State does not exist, first run' -tip = os.popen('hg tip | head -1 | cut -f 2 -d :').read().strip() +tip = os.popen('hg tip --template "{rev}"').read() print 'tip is', tip # Calculate the branches print 'analysing the branches...' hgchildren["0"] = () +hgparents["0"] = (None, None) hgbranch["0"] = "master" for cset in range(1, int(tip) + 1): hgchildren[str(cset)] = () - prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines() - if len(prnts) > 0: + prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().split(' ') + prnts = map(lambda x: x[:x.find(':')], prnts) + if prnts[0] != '': parent = prnts[0].strip() else: parent = str(cset - 1) @@ -120,6 +124,8 @@ for cset in range(1, int(tip) + 1): else: mparent = None + hgparents[str(cset)] = (parent, mparent) + if mparent: # For merge changesets, take either one, preferably the 'master' branch if hgbranch[mparent] == 'master': @@ -147,34 +153,27 @@ for cset in range(int(tip) + 1): hgnewcsets += 1 # get info - prnts = os.popen('hg log -r %d | grep ^parent: | cut -f 2 -d :' % cset).readlines() - if len(prnts) > 0: - parent = prnts[0].strip() - else: - parent = str(cset - 1) - if len(prnts) > 1: - mparent = prnts[1].strip() - else: - mparent = None - + log_data = os.popen('hg log -r %d --template "{tags}\n{date|date}\n{author}\n"' % cset).readlines() + tag = log_data[0].strip() + date = log_data[1].strip() + user = log_data[2].strip() + parent = hgparents[str(cset)][0] + mparent = hgparents[str(cset)][1] + + #get comment (fdcomment, filecomment) = tempfile.mkstemp() - csetcomment = os.popen('hg log -r %d -v | grep -v ^changeset: | grep -v ^parent: | grep -v ^user: | grep -v ^date | grep -v ^files: | grep -v ^description: | grep -v ^tag:' % cset).read().strip() + csetcomment = os.popen('hg log -r %d --template "{desc}"' % cset).read().strip() os.write(fdcomment, csetcomment) os.close(fdcomment) - date = os.popen('hg log -r %d | grep ^date: | cut -f 2- -d :' % cset).read().strip() - - tag = os.popen('hg log -r %d | grep ^tag: | cut -f 2- -d :' % cset).read().strip() - - user = os.popen('hg log -r %d | grep ^user: | cut -f 2- -d :' % cset).read().strip() - print '-----------------------------------------' print 'cset:', cset print 'branch:', hgbranch[str(cset)] print 'user:', user print 'date:', date print 'comment:', csetcomment - print 'parent:', parent + if parent: + print 'parent:', parent if mparent: print 'mparent:', mparent if tag: @@ -224,8 +223,7 @@ for cset in range(int(tip) + 1): os.system('git-branch -d %s' % otherbranch) # retrieve and record the version - vvv = os.popen('git-show | head -1').read() - vvv = vvv[vvv.index(' ') + 1 : ].strip() + vvv = os.popen('git-show --quiet --pretty=format:%H').read() print 'record', cset, '->', vvv hgvers[str(cset)] = vvv -- cgit v1.2.3 From 5c66d0d4580196094e80c552f141525759a8e249 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 17 Jan 2008 22:52:40 -0800 Subject: Officially deprecate repo-config. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- contrib/examples/git-tag.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 343364de04..9b0033d17c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -333,7 +333,7 @@ __git_commands () read-tree) : plumbing;; receive-pack) : plumbing;; reflog) : plumbing;; - repo-config) : plumbing;; + repo-config) : deprecated;; rerere) : plumbing;; rev-list) : plumbing;; rev-parse) : plumbing;; diff --git a/contrib/examples/git-tag.sh b/contrib/examples/git-tag.sh index ae7c531666..e9f3a228af 100755 --- a/contrib/examples/git-tag.sh +++ b/contrib/examples/git-tag.sh @@ -167,7 +167,7 @@ type=$(git cat-file -t $object) || exit 1 tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1 test -n "$username" || - username=$(git repo-config user.signingkey) || + username=$(git config user.signingkey) || username=$(expr "z$tagger" : 'z\(.*>\)') trap 'rm -f "$GIT_DIR"/TAG_TMP* "$GIT_DIR"/TAG_FINALMSG "$GIT_DIR"/TAG_EDITMSG' 0 -- cgit v1.2.3 From a3b811a4914cf02bb25662e330a067c1b0ddbc75 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 20 Jan 2008 00:54:57 -0600 Subject: Update git-completion for new 'remote rm' option Signed-off-by: Dan McGee Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9b0033d17c..0d33f9a3dc 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -970,18 +970,18 @@ _git_remote () while [ $c -lt $COMP_CWORD ]; do i="${COMP_WORDS[c]}" case "$i" in - add|show|prune|update) command="$i"; break ;; + add|rm|show|prune|update) command="$i"; break ;; esac c=$((++c)) done if [ $c -eq $COMP_CWORD -a -z "$command" ]; then - __gitcomp "add show prune update" + __gitcomp "add rm show prune update" return fi case "$command" in - show|prune) + rm|show|prune) __gitcomp "$(__git_remotes)" ;; update) -- cgit v1.2.3 From f3e9512be1c7935b8ca496f3687c6850b32a06f3 Mon Sep 17 00:00:00 2001 From: Jason McMullan Date: Wed, 5 Dec 2007 12:16:56 -0500 Subject: Remove $Id: ..$ $Header: ..$ etc from +ko and +k files during import This patch removes the '$Keyword: ...$' '...' data, so that files don't have spurious megre conflicts between branches. Handles both +ko and +k styles, and leaves the '$Foo$' in the original file. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c80a6da252..31c5501d11 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -964,9 +964,13 @@ class P4Sync(Command): stat = filedata[j] j += 1 text = '' - while j < len(filedata) and filedata[j]['code'] in ('text', - 'binary'): - text += filedata[j]['data'] + while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'): + tmp = filedata[j]['data'] + if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): + tmp = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', tmp) + elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): + tmp = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', tmp) + text += tmp j += 1 -- cgit v1.2.3 From e96e400f67b8478a99f9d2b5d82f08cd21f51a88 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 4 Jan 2008 14:27:55 +0100 Subject: git-p4: Fix submit user-interface. Don't ask any questions when submitting, behave similar to git-svn dcommit. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 85 ++++++++++++++-------------------------------- 1 file changed, 26 insertions(+), 59 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 31c5501d11..a74aabdb28 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -469,9 +469,7 @@ class P4Submit(Command): optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), optparse.make_option("--log-substitutions", dest="substFile"), - optparse.make_option("--dry-run", action="store_true"), optparse.make_option("--direct", dest="directSubmit", action="store_true"), - optparse.make_option("--trust-me-like-a-fool", dest="trustMeLikeAFool", action="store_true"), optparse.make_option("-M", dest="detectRename", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." @@ -479,12 +477,10 @@ class P4Submit(Command): self.firstTime = True self.reset = False self.interactive = True - self.dryRun = False self.substFile = "" self.firstTime = True self.origin = "" self.directSubmit = False - self.trustMeLikeAFool = False self.detectRename = False self.verbose = False self.isWindows = (platform.system() == "Windows") @@ -681,57 +677,30 @@ class P4Submit(Command): separatorLine += "\r" separatorLine += "\n" - response = "e" - if self.trustMeLikeAFool: - response = "y" - - firstIteration = True - while response == "e": - if not firstIteration: - response = raw_input("Do you want to submit this change? [y]es/[e]dit/[n]o/[s]kip ") - firstIteration = False - if response == "e": - [handle, fileName] = tempfile.mkstemp() - tmpFile = os.fdopen(handle, "w+") - tmpFile.write(submitTemplate + separatorLine + diff) - tmpFile.close() - defaultEditor = "vi" - if platform.system() == "Windows": - defaultEditor = "notepad" - editor = os.environ.get("EDITOR", defaultEditor); - system(editor + " " + fileName) - tmpFile = open(fileName, "rb") - message = tmpFile.read() - tmpFile.close() - os.remove(fileName) - submitTemplate = message[:message.index(separatorLine)] - if self.isWindows: - submitTemplate = submitTemplate.replace("\r\n", "\n") - - if response == "y" or response == "yes": - if self.dryRun: - print submitTemplate - raw_input("Press return to continue...") - else: - if self.directSubmit: - print "Submitting to git first" - os.chdir(self.oldWorkingDirectory) - write_pipe("git commit -a -F -", submitTemplate) - os.chdir(self.clientPath) - - write_pipe("p4 submit -i", submitTemplate) - elif response == "s": - for f in editedFiles: - system("p4 revert \"%s\"" % f); - for f in filesToAdd: - system("p4 revert \"%s\"" % f); - system("rm %s" %f) - for f in filesToDelete: - system("p4 delete \"%s\"" % f); - return - else: - print "Not submitting!" - self.interactive = False + [handle, fileName] = tempfile.mkstemp() + tmpFile = os.fdopen(handle, "w+") + tmpFile.write(submitTemplate + separatorLine + diff) + tmpFile.close() + defaultEditor = "vi" + if platform.system() == "Windows": + defaultEditor = "notepad" + editor = os.environ.get("EDITOR", defaultEditor); + system(editor + " " + fileName) + tmpFile = open(fileName, "rb") + message = tmpFile.read() + tmpFile.close() + os.remove(fileName) + submitTemplate = message[:message.index(separatorLine)] + if self.isWindows: + submitTemplate = submitTemplate.replace("\r\n", "\n") + + if self.directSubmit: + print "Submitting to git first" + os.chdir(self.oldWorkingDirectory) + write_pipe("git commit -a -F -", submitTemplate) + os.chdir(self.clientPath) + + write_pipe("p4 submit -i", submitTemplate) else: fileName = "submit.txt" file = open(fileName, "w+") @@ -828,10 +797,8 @@ class P4Submit(Command): sync = P4Sync() sync.run([]) - response = raw_input("Do you want to rebase current HEAD from Perforce now using git-p4 rebase? [y]es/[n]o ") - if response == "y" or response == "yes": - rebase = P4Rebase() - rebase.rebase() + rebase = P4Rebase() + rebase.rebase() os.remove(self.configFile) return True -- cgit v1.2.3 From 36ee4ee40e1851442b0b075870cd5de5df6ee2b6 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 7 Jan 2008 14:21:45 +0100 Subject: git-p4: Ensure the working directory and the index are clean before "git-p4 rebase" Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a74aabdb28..e5fe5f6d3d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1611,6 +1611,11 @@ class P4Rebase(Command): return self.rebase() def rebase(self): + if os.system("git update-index --refresh") != 0: + die("Some files in your working directory are modified and different than what is in your index. You can use git update-index to bring the index up-to-date or stash away all your changes with git stash."); + if len(read_pipe("git diff-index HEAD --")) > 0: + die("You have uncommited changes. Please commit them before rebasing or stash them away with git stash."); + [upstream, settings] = findUpstreamBranchPoint() if len(upstream) == 0: die("Cannot find upstream branchpoint for rebase") -- cgit v1.2.3 From 147402a2e9e29efe3fe75b5409cc2dd88cef04c7 Mon Sep 17 00:00:00 2001 From: Tommy Thorn Date: Sat, 2 Feb 2008 00:11:44 -0800 Subject: git-p4: Fix an obvious typo The regexp "$," can't match anything. Clearly not intended. This was introduced in ce6f33c8 which is quite a while ago. Signed-off-by: Tommy Thorn Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e5fe5f6d3d..c17afae94a 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1646,7 +1646,7 @@ class P4Clone(P4Sync): depotPath = args[0] depotDir = re.sub("(@[^@]*)$", "", depotPath) depotDir = re.sub("(#[^#]*)$", "", depotDir) - depotDir = re.sub(r"\.\.\.$,", "", depotDir) + depotDir = re.sub(r"\.\.\.$", "", depotDir) depotDir = re.sub(r"/$", "", depotDir) return os.path.split(depotDir)[1] -- cgit v1.2.3 From e75201963f6722ad84491c8f17dff8406ec0a198 Mon Sep 17 00:00:00 2001 From: Robin Rosenberg Date: Sun, 30 Sep 2007 02:20:45 +0200 Subject: Improve bash prompt to detect various states like an unfinished merge This patch makes the git prompt (when enabled) show if a merge or a rebase is unfinished. It also detects if a bisect is being done as well as detached checkouts. An uncompleted git-am cannot be distinguised from a rebase (the non-interactive version). Instead of having an even longer prompt we simply ignore that and hope the power users that use git-am knows the difference. Signed-off-by: Robin Rosenberg Acked-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 37 ++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0d33f9a3dc..4ea727b143 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -64,12 +64,41 @@ __gitdir () __git_ps1 () { - local b="$(git symbolic-ref HEAD 2>/dev/null)" - if [ -n "$b" ]; then + local g="$(git rev-parse --git-dir 2>/dev/null)" + if [ -n "$g" ]; then + local r + local b + if [ -d "$g/../.dotest" ] + then + r="|AM/REBASE" + b="$(git symbolic-ref HEAD 2>/dev/null)" + elif [ -f "$g/.dotest-merge/interactive" ] + then + r="|REBASE-i" + b="$(cat $g/.dotest-merge/head-name)" + elif [ -d "$g/.dotest-merge" ] + then + r="|REBASE-m" + b="$(cat $g/.dotest-merge/head-name)" + elif [ -f "$g/MERGE_HEAD" ] + then + r="|MERGING" + b="$(git symbolic-ref HEAD 2>/dev/null)" + else + if [ -f $g/BISECT_LOG ] + then + r="|BISECTING" + fi + if ! b="$(git symbolic-ref HEAD 2>/dev/null)" + then + b="$(cut -c1-7 $g/HEAD)..." + fi + fi + if [ -n "$1" ]; then - printf "$1" "${b##refs/heads/}" + printf "$1" "${b##refs/heads/}$r" else - printf " (%s)" "${b##refs/heads/}" + printf " (%s)" "${b##refs/heads/}$r" fi fi } -- cgit v1.2.3 From 053d9e432be246a389fb8adaa0c88e7c791f8b21 Mon Sep 17 00:00:00 2001 From: Toby Allsopp Date: Tue, 5 Feb 2008 09:41:43 +1300 Subject: git-p4: Fix indentation from tab to spaces Signed-off-by: Toby Allsopp --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c17afae94a..781a0cbbbc 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1646,7 +1646,7 @@ class P4Clone(P4Sync): depotPath = args[0] depotDir = re.sub("(@[^@]*)$", "", depotPath) depotDir = re.sub("(#[^#]*)$", "", depotDir) - depotDir = re.sub(r"\.\.\.$", "", depotDir) + depotDir = re.sub(r"\.\.\.$", "", depotDir) depotDir = re.sub(r"/$", "", depotDir) return os.path.split(depotDir)[1] -- cgit v1.2.3 From 3f3d564aa74e80cb30ead37e4581ee21220b4ac4 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 7 Feb 2008 13:50:19 +0100 Subject: git.el: Support for showing unknown/ignored directories. Instead of recursing into directories that only contain unknown files, display only the directory itself. Its contents can be expanded with git-find-file (bound to C-m). Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d8a06381f4..58d72a55c1 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -558,12 +558,15 @@ and returns the process output as a string." (?\100 " (type change file -> subproject)") (?\120 " (type change symlink -> subproject)") (t " (subproject)"))) + (?\110 nil) ;; directory (internal, not a real git state) (?\000 ;; deleted or unknown (case old-type (?\120 " (symlink)") (?\160 " (subproject)"))) (t (format " (unknown type %o)" new-type))))) - (if str (propertize str 'face 'git-status-face) ""))) + (cond (str (propertize str 'face 'git-status-face)) + ((eq new-type ?\110) "/") + (t "")))) (defun git-rename-as-string (info) "Return a string describing the copy or rename associated with INFO, or an empty string if none." @@ -666,9 +669,11 @@ Return the list of files that haven't been handled." (with-temp-buffer (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files)) (goto-char (point-min)) - (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) + (while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1) (let ((name (match-string 1))) - (push (git-create-fileinfo default-state name) infolist) + (push (git-create-fileinfo default-state name 0 + (if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0)) + infolist) (setq files (delete name files))))) (git-insert-info-list status infolist) files)) @@ -713,7 +718,7 @@ Return the list of files that haven't been handled." (defun git-run-ls-files-with-excludes (status files default-state &rest options) "Run git-ls-files on FILES with appropriate --exclude-from options." (let ((exclude-files (git-get-exclude-files))) - (apply #'git-run-ls-files status files default-state + (apply #'git-run-ls-files status files default-state "--directory" (concat "--exclude-per-directory=" git-per-dir-ignore-file) (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) @@ -957,6 +962,7 @@ Return the list of files that haven't been handled." "Add marked file(s) to the index cache." (interactive) (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored)))) + ;; FIXME: add support for directories (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) (apply #'git-call-process-env nil nil "update-index" "--add" "--" files) @@ -983,7 +989,10 @@ Return the list of files that haven't been handled." (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" ""))) (progn (dolist (name files) - (when (file-exists-p name) (delete-file name))) + (ignore-errors + (if (file-directory-p name) + (delete-directory name) + (delete-file name)))) (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files) (git-update-status-files files nil) (git-success-message "Removed" files)) @@ -992,7 +1001,7 @@ Return the list of files that haven't been handled." (defun git-revert-file () "Revert changes to the marked file(s)." (interactive) - (let ((files (git-marked-files)) + (let ((files (git-marked-files-state 'added 'deleted 'modified 'unmerged)) added modified) (when (and files (yes-or-no-p @@ -1063,6 +1072,16 @@ Return the list of files that haven't been handled." (message "Inserting unknown files...done")) (git-remove-handled))) +(defun git-expand-directory (info) + "Expand the directory represented by INFO to list its files." + (when (eq (lsh (git-fileinfo->new-perm info) -9) ?\110) + (let ((dir (git-fileinfo->name info))) + (git-set-filenames-state git-status (list dir) nil) + (git-run-ls-files-with-excludes git-status (list (concat dir "/")) 'unknown "-o") + (git-refresh-files) + (git-refresh-ewoc-hf git-status) + t))) + (defun git-setup-diff-buffer (buffer) "Setup a buffer for displaying a diff." (let ((dir default-directory)) @@ -1237,9 +1256,10 @@ Return the list of files that haven't been handled." (interactive) (unless git-status (error "Not in git-status buffer.")) (let ((info (ewoc-data (ewoc-locate git-status)))) - (find-file (git-fileinfo->name info)) - (when (eq 'unmerged (git-fileinfo->state info)) - (smerge-mode 1)))) + (unless (git-expand-directory info) + (find-file (git-fileinfo->name info)) + (when (eq 'unmerged (git-fileinfo->state info)) + (smerge-mode 1))))) (defun git-find-file-other-window () "Visit the current file in its own buffer in another window." -- cgit v1.2.3 From 76127b3a0d7cf359b12be80971f023b6ee07f7f9 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 7 Feb 2008 13:50:39 +0100 Subject: git.el: Added a command to amend a commit. It reverts the commit and sets up the status and edit log buffer to allow making changes and recommitting it. Bound to C-c C-a. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 58d72a55c1..5519ed107a 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -740,6 +740,27 @@ Return the list of files that haven't been handled." (git-refresh-files) (git-refresh-ewoc-hf git-status))) +(defun git-mark-files (status files) + "Mark all the specified FILES, and unmark the others." + (setq files (sort files #'string-lessp)) + (let ((file (and files (pop files))) + (node (ewoc-nth status 0))) + (while node + (let ((info (ewoc-data node))) + (if (and file (string-equal (git-fileinfo->name info) file)) + (progn + (unless (git-fileinfo->marked info) + (setf (git-fileinfo->marked info) t) + (setf (git-fileinfo->needs-refresh info) t)) + (setq file (pop files)) + (setq node (ewoc-next status node))) + (when (git-fileinfo->marked info) + (setf (git-fileinfo->marked info) nil) + (setf (git-fileinfo->needs-refresh info) t)) + (if (and file (string-lessp file (git-fileinfo->name info))) + (setq file (pop files)) + (setq node (ewoc-next status node)))))))) + (defun git-marked-files () "Return a list of all marked files, or if none a list containing just the file at cursor position." (unless git-status (error "Not in git-status buffer.")) @@ -1218,7 +1239,8 @@ Return the list of files that haven't been handled." (goto-char (point-min)) (when (re-search-forward "\n+\\'" nil t) (replace-match "\n" t t)) - (when sign-off (git-append-sign-off committer-name committer-email))))) + (when sign-off (git-append-sign-off committer-name committer-email))) + buffer)) (defun git-commit-file () "Commit the marked file(s), asking for a commit message." @@ -1251,6 +1273,52 @@ Return the list of files that haven't been handled." (setq buffer-file-coding-system coding-system) (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) +(defun git-setup-commit-buffer (commit) + "Setup the commit buffer with the contents of COMMIT." + (let (author-name author-email subject date msg) + (with-temp-buffer + (let ((coding-system (git-get-logoutput-coding-system))) + (git-call-process-env t nil "log" "-1" commit) + (goto-char (point-min)) + (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t) + (setq author-name (match-string 1)) + (setq author-email (match-string 2))) + (when (re-search-forward "^Date: *\\(.*\\)$" nil t) + (setq date (match-string 1))) + (while (re-search-forward "^ \\(.*\\)$" nil t) + (push (match-string 1) msg)) + (setq msg (nreverse msg)) + (setq subject (pop msg)) + (while (and msg (zerop (length (car msg))) (pop msg))))) + (git-setup-log-buffer (get-buffer-create "*git-commit*") + author-name author-email subject date + (mapconcat #'identity msg "\n")))) + +(defun git-get-commit-files (commit) + "Retrieve the list of files modified by COMMIT." + (let (files) + (with-temp-buffer + (git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit) + (goto-char (point-min)) + (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) + (push (match-string 1) files))) + files)) + +(defun git-amend-commit () + "Undo the last commit on HEAD, and set things up to commit an +amended version of it." + (interactive) + (unless git-status (error "Not in git-status buffer.")) + (when (git-empty-db-p) (error "No commit to amend.")) + (let* ((commit (git-rev-parse "HEAD")) + (files (git-get-commit-files commit))) + (git-call-process-env nil nil "reset" "--soft" "HEAD^") + (git-update-status-files (copy-sequence files) 'uptodate) + (git-mark-files git-status files) + (git-refresh-files) + (git-setup-commit-buffer commit) + (git-commit-file))) + (defun git-find-file () "Visit the current file in its own buffer." (interactive) @@ -1329,6 +1397,7 @@ Return the list of files that haven't been handled." (unless git-status-mode-map (let ((map (make-keymap)) + (commit-map (make-sparse-keymap)) (diff-map (make-sparse-keymap)) (toggle-map (make-sparse-keymap))) (suppress-keymap map) @@ -1337,6 +1406,7 @@ Return the list of files that haven't been handled." (define-key map " " 'git-next-file) (define-key map "a" 'git-add-file) (define-key map "c" 'git-commit-file) + (define-key map "\C-c" commit-map) (define-key map "d" diff-map) (define-key map "=" 'git-diff-file) (define-key map "f" 'git-find-file) @@ -1362,6 +1432,8 @@ Return the list of files that haven't been handled." (define-key map "x" 'git-remove-handled) (define-key map "\C-?" 'git-unmark-file-up) (define-key map "\M-\C-?" 'git-unmark-all) + ; the commit submap + (define-key commit-map "\C-a" 'git-amend-commit) ; the diff submap (define-key diff-map "b" 'git-diff-file-base) (define-key diff-map "c" 'git-diff-file-combined) -- cgit v1.2.3 From 928323af6b761e8b1c33ce98e67c2f56fd1b7997 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 7 Feb 2008 13:51:20 +0100 Subject: git.el: Check for existing buffers on revert. Refuse to revert a file if it is modified in an existing buffer but not saved. On success, revert the buffers that contains the files that have been reverted. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 5519ed107a..e1058b9a99 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1033,11 +1033,19 @@ Return the list of files that haven't been handled." ('deleted (push (git-fileinfo->name info) modified)) ('unmerged (push (git-fileinfo->name info) modified)) ('modified (push (git-fileinfo->name info) modified)))) + ;; check if a buffer contains one of the files and isn't saved + (dolist (file (append added modified)) + (let ((buffer (get-file-buffer file))) + (when (and buffer (buffer-modified-p buffer)) + (error "Buffer %s is modified. Please kill or save modified buffers before reverting." (buffer-name buffer))))) (when added (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added)) (when modified (apply #'git-call-process-env nil nil "checkout" "HEAD" modified)) (git-update-status-files (append added modified) 'uptodate) + (dolist (file (append added modified)) + (let ((buffer (get-file-buffer file))) + (when buffer (with-current-buffer buffer (revert-buffer t t t))))) (git-success-message "Reverted" (git-get-filenames files))))) (defun git-resolve-file () -- cgit v1.2.3 From 0520e2154fb6a7418663bdee839c3448d51b9ae4 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Thu, 7 Feb 2008 13:51:34 +0100 Subject: git.el: Better handling of subprocess errors. Where possible, capture the output of the git command and display it if the command fails. Signed-off-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 88 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 37 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index e1058b9a99..a8bf0ef883 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -35,7 +35,6 @@ ;; ;; TODO ;; - portability to XEmacs -;; - better handling of subprocess errors ;; - diff against other branch ;; - renaming files from the status buffer ;; - creating tags @@ -191,6 +190,18 @@ if there is already one that displays the same directory." (append (git-get-env-strings env) (list "git") args)) (apply #'call-process "git" nil buffer nil args))) +(defun git-call-process-display-error (&rest args) + "Wrapper for call-process that displays error messages." + (let* ((dir default-directory) + (buffer (get-buffer-create "*Git Command Output*")) + (ok (with-current-buffer buffer + (let ((default-directory dir) + (buffer-read-only nil)) + (erase-buffer) + (eq 0 (apply 'call-process "git" nil (list buffer t) nil args)))))) + (unless ok (display-message-or-buffer buffer)) + ok)) + (defun git-call-process-env-string (env &rest args) "Wrapper for call-process that sets environment strings, and returns the process output as a string." @@ -377,7 +388,7 @@ and returns the process output as a string." (when reason (push reason args) (push "-m" args)) - (eq 0 (apply #'git-call-process-env nil nil "update-ref" args)))) + (apply 'git-call-process-display-error "update-ref" args))) (defun git-read-tree (tree &optional index-file) "Read a tree into the index file." @@ -866,16 +877,17 @@ Return the list of files that haven't been handled." (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))) - (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)) - (git-update-status-files (git-get-filenames files) 'uptodate) - (git-call-process-env nil nil "rerere") - (git-call-process-env nil nil "gc" "--auto") - (git-refresh-files) - (git-refresh-ewoc-hf git-status) - (message "Committed %s." commit) - (git-run-hook "post-commit" nil)) + (when commit + (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)) + (git-update-status-files (git-get-filenames files) 'uptodate) + (git-call-process-env nil nil "rerere") + (git-call-process-env nil nil "gc" "--auto") + (git-refresh-files) + (git-refresh-ewoc-hf git-status) + (message "Committed %s." commit) + (git-run-hook "post-commit" nil))) (message "Commit aborted.")))) (message "No files to commit."))) (delete-file index-file)))))) @@ -986,9 +998,9 @@ Return the list of files that haven't been handled." ;; FIXME: add support for directories (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) - (apply #'git-call-process-env nil nil "update-index" "--add" "--" files) - (git-update-status-files files 'uptodate) - (git-success-message "Added" files))) + (when (apply 'git-call-process-display-error "update-index" "--add" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Added" files)))) (defun git-ignore-file () "Add marked file(s) to the ignore list." @@ -1014,9 +1026,9 @@ Return the list of files that haven't been handled." (if (file-directory-p name) (delete-directory name) (delete-file name)))) - (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files) - (git-update-status-files files nil) - (git-success-message "Removed" files)) + (when (apply 'git-call-process-display-error "update-index" "--remove" "--" files) + (git-update-status-files files nil) + (git-success-message "Removed" files))) (message "Aborting")))) (defun git-revert-file () @@ -1034,28 +1046,30 @@ Return the list of files that haven't been handled." ('unmerged (push (git-fileinfo->name info) modified)) ('modified (push (git-fileinfo->name info) modified)))) ;; check if a buffer contains one of the files and isn't saved - (dolist (file (append added modified)) + (dolist (file modified) (let ((buffer (get-file-buffer file))) (when (and buffer (buffer-modified-p buffer)) (error "Buffer %s is modified. Please kill or save modified buffers before reverting." (buffer-name buffer))))) - (when added - (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added)) - (when modified - (apply #'git-call-process-env nil nil "checkout" "HEAD" modified)) - (git-update-status-files (append added modified) 'uptodate) - (dolist (file (append added modified)) - (let ((buffer (get-file-buffer file))) - (when buffer (with-current-buffer buffer (revert-buffer t t t))))) - (git-success-message "Reverted" (git-get-filenames files))))) + (let ((ok (and + (or (not added) + (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added)) + (or (not modified) + (apply 'git-call-process-display-error "checkout" "HEAD" modified))))) + (git-update-status-files (append added modified) 'uptodate) + (when ok + (dolist (file modified) + (let ((buffer (get-file-buffer file))) + (when buffer (with-current-buffer buffer (revert-buffer t t t))))) + (git-success-message "Reverted" (git-get-filenames files))))))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." (interactive) (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) (when files - (apply #'git-call-process-env nil nil "update-index" "--" files) - (git-update-status-files files 'uptodate) - (git-success-message "Resolved" files)))) + (when (apply 'git-call-process-display-error "update-index" "--" files) + (git-update-status-files files 'uptodate) + (git-success-message "Resolved" files))))) (defun git-remove-handled () "Remove handled files from the status list." @@ -1320,12 +1334,12 @@ amended version of it." (when (git-empty-db-p) (error "No commit to amend.")) (let* ((commit (git-rev-parse "HEAD")) (files (git-get-commit-files commit))) - (git-call-process-env nil nil "reset" "--soft" "HEAD^") - (git-update-status-files (copy-sequence files) 'uptodate) - (git-mark-files git-status files) - (git-refresh-files) - (git-setup-commit-buffer commit) - (git-commit-file))) + (when (git-call-process-display-error "reset" "--soft" "HEAD^") + (git-update-status-files (copy-sequence files) 'uptodate) + (git-mark-files git-status files) + (git-refresh-files) + (git-setup-commit-buffer commit) + (git-commit-file)))) (defun git-find-file () "Visit the current file in its own buffer." -- cgit v1.2.3 From 24a2293ad35d567530048f0d2b0d11e0012af26d Mon Sep 17 00:00:00 2001 From: Junichi Uekawa Date: Tue, 12 Feb 2008 00:00:07 +0900 Subject: git-blame.el: show the when, who and what in the minibuffer. Change the default operation to show 'when (day the commit was made), who (who made the commit), what (what the commit log was)' in the minibuffer instead of SHA1 and title of the commit log. Since the user may prefer other displaying options, it is made as a user-configurable option. Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index bb671d561e..9f92cd250b 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -105,6 +105,13 @@ selected element from l." (setq ,l (remove e ,l)) e)) +(defvar git-blame-log-oneline-format + "format:[%cr] %cn: %s" + "*Formatting option used for describing current line in the minibuffer. + +This option is used to pass to git log --pretty= command-line option, +and describe which commit the current line was made.") + (defvar git-blame-dark-colors (git-blame-color-scale "0c" "04" "24" "1c" "2c" "34" "14" "3c") "*List of colors (format #RGB) to use in a dark environment. @@ -371,7 +378,8 @@ See also function `git-blame-mode'." (defun git-describe-commit (hash) (with-temp-buffer (call-process "git" nil t nil - "log" "-1" "--pretty=oneline" + "log" "-1" + (concat "--pretty=" git-blame-log-oneline-format) hash) (buffer-substring (point-min) (1- (point-max))))) -- cgit v1.2.3 From 13bf1a99764ea751f6fa75502309d8b91529623a Mon Sep 17 00:00:00 2001 From: Stelian Pop Date: Fri, 15 Feb 2008 22:20:44 +0100 Subject: hg-to-git: fix parent analysis Fix a bug in the hg-to-git convertor introduced by commit 1bc7c13af9f936aa80893100120b542338a10bf4: when searching the changeset parents, 'hg log' returns an extra space at the end of the line, which confuses the .split(' ') based tokenizer: Traceback (most recent call last): File "hg-to-git.py", line 123, in hgchildren[mparent] += ( str(cset), ) KeyError: '' Signed-off-by: Stelian Pop Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index c35b15860d..d72ffbb777 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -111,7 +111,7 @@ hgparents["0"] = (None, None) hgbranch["0"] = "master" for cset in range(1, int(tip) + 1): hgchildren[str(cset)] = () - prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().split(' ') + prnts = os.popen('hg log -r %d --template "{parents}"' % cset).read().strip().split(' ') prnts = map(lambda x: x[:x.find(':')], prnts) if prnts[0] != '': parent = prnts[0].strip() -- cgit v1.2.3 From 782c2d65c24066a5d83453efb52763bc34c10f81 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Thu, 7 Feb 2008 11:40:23 -0500 Subject: Build in checkout The only differences in behavior should be: - git checkout -m with non-trivial merging won't print out merge-recursive messages (see the change in t7201-co.sh) - git checkout -- paths... will give a sensible error message if HEAD is invalid as a commit. - some intermediate states which were written to disk in the shell version (in particular, index states) are only kept in memory in this version, and therefore these can no longer be revealed by later write operations becoming impossible. - when we change branches, we discard MERGE_MSG, SQUASH_MSG, and rr-cache/MERGE_RR, like reset always has. I'm not 100% sure I got the merge recursive setup exactly right; the base for a non-trivial merge in the shell code doesn't seem theoretically justified to me, but I tried to match it anyway, and the tests all pass this way. Other than these items, the results should be identical to the shell version, so far as I can tell. [jc: squashed lock-file fix from Dscho in] Signed-off-by: Daniel Barkalow Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/examples/git-checkout.sh | 298 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100755 contrib/examples/git-checkout.sh (limited to 'contrib') diff --git a/contrib/examples/git-checkout.sh b/contrib/examples/git-checkout.sh new file mode 100755 index 0000000000..5621c69d86 --- /dev/null +++ b/contrib/examples/git-checkout.sh @@ -0,0 +1,298 @@ +#!/bin/sh + +OPTIONS_KEEPDASHDASH=t +OPTIONS_SPEC="\ +git-checkout [options] [] [...] +-- +b= create a new branch started at +l create the new branch's reflog +track arrange that the new branch tracks the remote branch +f proceed even if the index or working tree is not HEAD +m merge local modifications into the new branch +q,quiet be quiet +" +SUBDIRECTORY_OK=Sometimes +. git-sh-setup +require_work_tree + +old_name=HEAD +old=$(git rev-parse --verify $old_name 2>/dev/null) +oldbranch=$(git symbolic-ref $old_name 2>/dev/null) +new= +new_name= +force= +branch= +track= +newbranch= +newbranch_log= +merge= +quiet= +v=-v +LF=' +' + +while test $# != 0; do + case "$1" in + -b) + shift + newbranch="$1" + [ -z "$newbranch" ] && + die "git checkout: -b needs a branch name" + git show-ref --verify --quiet -- "refs/heads/$newbranch" && + die "git checkout: branch $newbranch already exists" + git check-ref-format "heads/$newbranch" || + die "git checkout: we do not like '$newbranch' as a branch name." + ;; + -l) + newbranch_log=-l + ;; + --track|--no-track) + track="$1" + ;; + -f) + force=1 + ;; + -m) + merge=1 + ;; + -q|--quiet) + quiet=1 + v= + ;; + --) + shift + break + ;; + *) + usage + ;; + esac + shift +done + +arg="$1" +if rev=$(git rev-parse --verify "$arg^0" 2>/dev/null) +then + [ -z "$rev" ] && die "unknown flag $arg" + new_name="$arg" + if git show-ref --verify --quiet -- "refs/heads/$arg" + then + rev=$(git rev-parse --verify "refs/heads/$arg^0") + branch="$arg" + fi + new="$rev" + shift +elif rev=$(git rev-parse --verify "$arg^{tree}" 2>/dev/null) +then + # checking out selected paths from a tree-ish. + new="$rev" + new_name="$arg^{tree}" + shift +fi +[ "$1" = "--" ] && shift + +case "$newbranch,$track" in +,--*) + die "git checkout: --track and --no-track require -b" +esac + +case "$force$merge" in +11) + die "git checkout: -f and -m are incompatible" +esac + +# The behaviour of the command with and without explicit path +# parameters is quite different. +# +# Without paths, we are checking out everything in the work tree, +# possibly switching branches. This is the traditional behaviour. +# +# With paths, we are _never_ switching branch, but checking out +# the named paths from either index (when no rev is given), +# or the named tree-ish (when rev is given). + +if test "$#" -ge 1 +then + hint= + if test "$#" -eq 1 + then + hint=" +Did you intend to checkout '$@' which can not be resolved as commit?" + fi + if test '' != "$newbranch$force$merge" + then + die "git checkout: updating paths is incompatible with switching branches/forcing$hint" + fi + if test '' != "$new" + then + # from a specific tree-ish; note that this is for + # rescuing paths and is never meant to remove what + # is not in the named tree-ish. + git ls-tree --full-name -r "$new" "$@" | + git update-index --index-info || exit $? + fi + + # Make sure the request is about existing paths. + git ls-files --full-name --error-unmatch -- "$@" >/dev/null || exit + git ls-files --full-name -- "$@" | + (cd_to_toplevel && git checkout-index -f -u --stdin) + + # Run a post-checkout hook -- the HEAD does not change so the + # current HEAD is passed in for both args + if test -x "$GIT_DIR"/hooks/post-checkout; then + "$GIT_DIR"/hooks/post-checkout $old $old 0 + fi + + exit $? +else + # Make sure we did not fall back on $arg^{tree} codepath + # since we are not checking out from an arbitrary tree-ish, + # but switching branches. + if test '' != "$new" + then + git rev-parse --verify "$new^{commit}" >/dev/null 2>&1 || + die "Cannot switch branch to a non-commit." + fi +fi + +# We are switching branches and checking out trees, so +# we *NEED* to be at the toplevel. +cd_to_toplevel + +[ -z "$new" ] && new=$old && new_name="$old_name" + +# If we don't have an existing branch that we're switching to, +# and we don't have a new branch name for the target we +# are switching to, then we are detaching our HEAD from any +# branch. However, if "git checkout HEAD" detaches the HEAD +# from the current branch, even though that may be logically +# correct, it feels somewhat funny. More importantly, we do not +# want "git checkout" nor "git checkout -f" to detach HEAD. + +detached= +detach_warn= + +describe_detached_head () { + test -n "$quiet" || { + printf >&2 "$1 " + GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2" -- + } +} + +if test -z "$branch$newbranch" && test "$new_name" != "$old_name" +then + detached="$new" + if test -n "$oldbranch" && test -z "$quiet" + then + detach_warn="Note: moving to \"$new_name\" which isn't a local branch +If you want to create a new branch from this checkout, you may do so +(now or later) by using -b with the checkout command again. Example: + git checkout -b " + fi +elif test -z "$oldbranch" && test "$new" != "$old" +then + describe_detached_head 'Previous HEAD position was' "$old" +fi + +if [ "X$old" = X ] +then + if test -z "$quiet" + then + echo >&2 "warning: You appear to be on a branch yet to be born." + echo >&2 "warning: Forcing checkout of $new_name." + fi + force=1 +fi + +if [ "$force" ] +then + git read-tree $v --reset -u $new +else + git update-index --refresh >/dev/null + merge_error=$(git read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || ( + case "$merge" in + '') + echo >&2 "$merge_error" + exit 1 ;; + esac + + # Match the index to the working tree, and do a three-way. + git diff-files --name-only | git update-index --remove --stdin && + work=`git write-tree` && + git read-tree $v --reset -u $new || exit + + eval GITHEAD_$new='${new_name:-${branch:-$new}}' && + eval GITHEAD_$work=local && + export GITHEAD_$new GITHEAD_$work && + git merge-recursive $old -- $new $work + + # Do not register the cleanly merged paths in the index yet. + # this is not a real merge before committing, but just carrying + # the working tree changes along. + unmerged=`git ls-files -u` + git read-tree $v --reset $new + case "$unmerged" in + '') ;; + *) + ( + z40=0000000000000000000000000000000000000000 + echo "$unmerged" | + sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /" + echo "$unmerged" + ) | git update-index --index-info + ;; + esac + exit 0 + ) + saved_err=$? + if test "$saved_err" = 0 && test -z "$quiet" + then + git diff-index --name-status "$new" + fi + (exit $saved_err) +fi + +# +# Switch the HEAD pointer to the new branch if we +# checked out a branch head, and remove any potential +# old MERGE_HEAD's (subsequent commits will clearly not +# be based on them, since we re-set the index) +# +if [ "$?" -eq 0 ]; then + if [ "$newbranch" ]; then + git branch $track $newbranch_log "$newbranch" "$new_name" || exit + branch="$newbranch" + fi + if test -n "$branch" + then + old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'` + GIT_DIR="$GIT_DIR" git symbolic-ref -m "checkout: moving from ${old_branch_name:-$old} to $branch" HEAD "refs/heads/$branch" + if test -n "$quiet" + then + true # nothing + elif test "refs/heads/$branch" = "$oldbranch" + then + echo >&2 "Already on branch \"$branch\"" + else + echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\"" + fi + elif test -n "$detached" + then + old_branch_name=`expr "z$oldbranch" : 'zrefs/heads/\(.*\)'` + git update-ref --no-deref -m "checkout: moving from ${old_branch_name:-$old} to $arg" HEAD "$detached" || + die "Cannot detach HEAD" + if test -n "$detach_warn" + then + echo >&2 "$detach_warn" + fi + describe_detached_head 'HEAD is now at' HEAD + fi + rm -f "$GIT_DIR/MERGE_HEAD" +else + exit 1 +fi + +# Run a post-checkout hook +if test -x "$GIT_DIR"/hooks/post-checkout; then + "$GIT_DIR"/hooks/post-checkout $old $new 1 +fi -- cgit v1.2.3 From f27e55864317611385be4d33b3c53ca787379df9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20K=C3=A5gedal?= Date: Tue, 19 Feb 2008 15:01:53 +0100 Subject: git.el: Set process-environment instead of invoking env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will make it a little less posix-dependent, and more efficient. Included is also a minor doc improvement. Signed-off-by: David Kågedal Acked-by: Alexandre Julliard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index a8bf0ef883..f69b697f8d 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -185,9 +185,8 @@ if there is already one that displays the same directory." (defun git-call-process-env (buffer env &rest args) "Wrapper for call-process that sets environment strings." - (if env - (apply #'call-process "env" nil buffer nil - (append (git-get-env-strings env) (list "git") args)) + (let ((process-environment (append (git-get-env-strings env) + process-environment))) (apply #'call-process "git" nil buffer nil args))) (defun git-call-process-display-error (&rest args) @@ -204,7 +203,7 @@ if there is already one that displays the same directory." (defun git-call-process-env-string (env &rest args) "Wrapper for call-process that sets environment strings, -and returns the process output as a string." +and returns the process output as a string, or nil if the git failed." (with-temp-buffer (and (eq 0 (apply #' git-call-process-env t env args)) (buffer-string)))) -- cgit v1.2.3 From 27c578885a0b8f56430c5a24f558e2b45cf04a38 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 24 Feb 2008 03:07:33 -0500 Subject: Use git-describe --exact-match in bash prompt on detached HEAD Most of the time when I am on a detached HEAD and I am not doing a rebase or bisect operation the working directory is sitting on a tagged release of the repository. Showing the tag name instead of the commit SHA-1 is much more descriptive and a much better reminder of the state of this working directory. Now that git-describe --exact-match is available as a cheap means of obtaining the exact annotated tag or nothing at all, we can favor the annotated tag name over the abbreviated commit SHA-1. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 4ea727b143..8722a68795 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -91,7 +91,10 @@ __git_ps1 () fi if ! b="$(git symbolic-ref HEAD 2>/dev/null)" then - b="$(cut -c1-7 $g/HEAD)..." + if ! b="$(git describe --exact-match HEAD 2>/dev/null)" + then + b="$(cut -c1-7 $g/HEAD)..." + fi fi fi -- cgit v1.2.3 From 354081d5a0392a015731a637f026dc885a4ddb0f Mon Sep 17 00:00:00 2001 From: Tommy Thorn Date: Sun, 3 Feb 2008 10:38:51 -0800 Subject: git-p4: support exclude paths Teach git-p4 about the -/ option which adds depot paths to the exclude list, used when cloning. The option is chosen such that the natural Perforce syntax works, eg: git p4 clone //branch/path/... -//branch/path/{large,old}/... Trailing ... on exclude paths are optional. This is a generalization of a change by Dmitry Kakurin (thanks). Signed-off-by: Tommy Thorn Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 781a0cbbbc..e4238538a1 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -843,18 +843,25 @@ class P4Sync(Command): self.keepRepoPath = False self.depotPaths = None self.p4BranchesInGit = [] + self.cloneExclude = [] if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False def extractFilesFromCommit(self, commit): + self.cloneExclude = [re.sub(r"\.\.\.$", "", path) + for path in self.cloneExclude] files = [] fnum = 0 while commit.has_key("depotFile%s" % fnum): path = commit["depotFile%s" % fnum] - found = [p for p in self.depotPaths - if path.startswith (p)] + if [p for p in self.cloneExclude + if path.startswith (p)]: + found = False + else: + found = [p for p in self.depotPaths + if path.startswith (p)] if not found: fnum = fnum + 1 continue @@ -1634,13 +1641,23 @@ class P4Clone(P4Sync): P4Sync.__init__(self) self.description = "Creates a new git repository and imports from Perforce into it" self.usage = "usage: %prog [options] //depot/path[@revRange]" - self.options.append( + self.options += [ optparse.make_option("--destination", dest="cloneDestination", action='store', default=None, - help="where to leave result of the clone")) + help="where to leave result of the clone"), + optparse.make_option("-/", dest="cloneExclude", + action="append", type="string", + help="exclude depot path") + ] self.cloneDestination = None self.needsGit = False + # This is required for the "append" cloneExclude action + def ensure_value(self, attr, value): + if not hasattr(self, attr) or getattr(self, attr) is None: + setattr(self, attr, value) + return getattr(self, attr) + def defaultDestination(self, args): ## TODO: use common prefix of args? depotPath = args[0] @@ -1664,6 +1681,7 @@ class P4Clone(P4Sync): self.cloneDestination = depotPaths[-1] depotPaths = depotPaths[:-1] + self.cloneExclude = ["/"+p for p in self.cloneExclude] for p in depotPaths: if not p.startswith("//"): return False -- cgit v1.2.3 From 4b61b5c963ad1fd59e858c2bc21b1b48836f04f8 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 19 Feb 2008 09:12:29 +0100 Subject: git-p4: Remove --log-substitutions feature. This turns out to be rarely useful and is already covered by git's commit.template configuration variable. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 7 ------- 1 file changed, 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e4238538a1..f2a6059a7e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -468,7 +468,6 @@ class P4Submit(Command): optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), - optparse.make_option("--log-substitutions", dest="substFile"), optparse.make_option("--direct", dest="directSubmit", action="store_true"), optparse.make_option("-M", dest="detectRename", action="store_true"), ] @@ -477,7 +476,6 @@ class P4Submit(Command): self.firstTime = True self.reset = False self.interactive = True - self.substFile = "" self.firstTime = True self.origin = "" self.directSubmit = False @@ -759,11 +757,6 @@ class P4Submit(Command): if self.reset: self.firstTime = True - if len(self.substFile) > 0: - for line in open(self.substFile, "r").readlines(): - tokens = line.strip().split("=") - self.logSubstitutions[tokens[0]] = tokens[1] - self.check() self.configFile = self.gitdir + "/p4-git-sync.cfg" self.config = shelve.open(self.configFile, writeback=True) -- cgit v1.2.3 From edae1e2f407d0e9c6c92333047bc31b27bfdd58f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 19 Feb 2008 09:29:06 +0100 Subject: git-p4: Clean up git-p4 submit's log message handling. Instead of trying to substitute fields in the p4 submit template we now simply replace the description of the submit with the log message of the git commit. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f2a6059a7e..e55a41b10e 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -483,10 +483,6 @@ class P4Submit(Command): self.verbose = False self.isWindows = (platform.system() == "Windows") - self.logSubstitutions = {} - self.logSubstitutions[""] = "%log%" - self.logSubstitutions["\tDetails:"] = "\tDetails: %log%" - def check(self): if len(p4CmdList("opened ...")) > 0: die("You have files opened with perforce! Close them before starting the sync.") @@ -507,26 +503,31 @@ class P4Submit(Command): self.config["commits"] = commits + # replaces everything between 'Description:' and the next P4 submit template field with the + # commit message def prepareLogMessage(self, template, message): result = "" + inDescriptionSection = False + for line in template.split("\n"): if line.startswith("#"): result += line + "\n" continue - substituted = False - for key in self.logSubstitutions.keys(): - if line.find(key) != -1: - value = self.logSubstitutions[key] - value = value.replace("%log%", message) - if value != "@remove@": - result += line.replace(key, value) + "\n" - substituted = True - break + if inDescriptionSection: + if line.startswith("Files:"): + inDescriptionSection = False + else: + continue + else: + if line.startswith("Description:"): + inDescriptionSection = True + line += "\n" + for messageLine in message.split("\n"): + line += "\t" + messageLine + "\n" - if not substituted: - result += line + "\n" + result += line + "\n" return result @@ -650,7 +651,6 @@ class P4Submit(Command): logMessage = "" if not self.directSubmit: logMessage = extractLogMessageFromGitCommit(id) - logMessage = logMessage.replace("\n", "\n\t") if self.isWindows: logMessage = logMessage.replace("\n", "\r\n") logMessage = logMessage.strip() -- cgit v1.2.3 From 0e36f2d726d4ce50c3f128bcc168d59ad03e804f Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 19 Feb 2008 09:33:08 +0100 Subject: git-p4: Removed git-p4 submit --direct. This feature was originally meant to allow for quicker direct submits into perforce, but it turns out that it is not actually quicker than doing a git commit and then running git-p4 submit. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 56 +++++++++------------------------------------- 1 file changed, 11 insertions(+), 45 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index e55a41b10e..087f4229a0 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -468,7 +468,6 @@ class P4Submit(Command): optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--origin", dest="origin"), optparse.make_option("--reset", action="store_true", dest="reset"), - optparse.make_option("--direct", dest="directSubmit", action="store_true"), optparse.make_option("-M", dest="detectRename", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." @@ -478,7 +477,6 @@ class P4Submit(Command): self.interactive = True self.firstTime = True self.origin = "" - self.directSubmit = False self.detectRename = False self.verbose = False self.isWindows = (platform.system() == "Windows") @@ -494,12 +492,9 @@ class P4Submit(Command): "maybe you want to call git-p4 submit --reset" % self.configFile) commits = [] - if self.directSubmit: - commits.append("0") - else: - for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): - commits.append(line.strip()) - commits.reverse() + for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): + commits.append(line.strip()) + commits.reverse() self.config["commits"] = commits @@ -556,13 +551,9 @@ class P4Submit(Command): return template def applyCommit(self, id): - if self.directSubmit: - print "Applying local change in working directory/index" - diff = self.diffStatus - else: - print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) - diffOpts = ("", "-M")[self.detectRename] - diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id)) + print "Applying %s" % (read_pipe("git log --max-count=1 --pretty=oneline %s" % id)) + diffOpts = ("", "-M")[self.detectRename] + diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id)) filesToAdd = set() filesToDelete = set() editedFiles = set() @@ -597,10 +588,7 @@ class P4Submit(Command): else: die("unknown modifier %s for %s" % (modifier, path)) - if self.directSubmit: - diffcmd = "cat \"%s\"" % self.diffFile - else: - diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id) + diffcmd = "git format-patch -k --stdout \"%s^\"..\"%s\"" % (id, id) patchcmd = diffcmd + " | git apply " tryPatchCmd = patchcmd + "--check -" applyPatchCmd = patchcmd + "--check --apply -" @@ -648,12 +636,10 @@ class P4Submit(Command): mode = filesToChangeExecBit[f] setP4ExecBit(f, mode) - logMessage = "" - if not self.directSubmit: - logMessage = extractLogMessageFromGitCommit(id) - if self.isWindows: - logMessage = logMessage.replace("\n", "\r\n") - logMessage = logMessage.strip() + logMessage = extractLogMessageFromGitCommit(id) + if self.isWindows: + logMessage = logMessage.replace("\n", "\r\n") + logMessage = logMessage.strip() template = self.prepareSubmitTemplate() @@ -692,12 +678,6 @@ class P4Submit(Command): if self.isWindows: submitTemplate = submitTemplate.replace("\r\n", "\n") - if self.directSubmit: - print "Submitting to git first" - os.chdir(self.oldWorkingDirectory) - write_pipe("git commit -a -F -", submitTemplate) - os.chdir(self.clientPath) - write_pipe("p4 submit -i", submitTemplate) else: fileName = "submit.txt" @@ -739,17 +719,6 @@ class P4Submit(Command): print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath) self.oldWorkingDirectory = os.getcwd() - if self.directSubmit: - self.diffStatus = read_pipe_lines("git diff -r --name-status HEAD") - if len(self.diffStatus) == 0: - print "No changes in working directory to submit." - return True - patch = read_pipe("git diff -p --binary --diff-filter=ACMRTUXB HEAD") - self.diffFile = self.gitdir + "/p4-git-diff" - f = open(self.diffFile, "wb") - f.write(patch) - f.close(); - os.chdir(self.clientPath) print "Syncronizing p4 checkout..." system("p4 sync ...") @@ -777,9 +746,6 @@ class P4Submit(Command): self.config.close() - if self.directSubmit: - os.remove(self.diffFile) - if len(commits) == 0: if self.firstTime: print "No changes found to apply between %s and current HEAD" % self.origin -- cgit v1.2.3 From 4c750c0d8bfd7e75b86d660def012bff17d2bf8e Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 19 Feb 2008 09:37:16 +0100 Subject: git-p4: git-p4 submit cleanups. Removed storing the list of commits in a configuration file. We only need the list of commits at run-time. Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 51 +++++++++------------------------------------- 1 file changed, 10 insertions(+), 41 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 087f4229a0..d59ed945de 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -464,18 +464,13 @@ class P4Submit(Command): def __init__(self): Command.__init__(self) self.options = [ - optparse.make_option("--continue", action="store_false", dest="firstTime"), optparse.make_option("--verbose", dest="verbose", action="store_true"), optparse.make_option("--origin", dest="origin"), - optparse.make_option("--reset", action="store_true", dest="reset"), optparse.make_option("-M", dest="detectRename", action="store_true"), ] self.description = "Submit changes from git to the perforce depot." self.usage += " [name of git branch to submit into perforce depot]" - self.firstTime = True - self.reset = False self.interactive = True - self.firstTime = True self.origin = "" self.detectRename = False self.verbose = False @@ -485,19 +480,6 @@ class P4Submit(Command): if len(p4CmdList("opened ...")) > 0: die("You have files opened with perforce! Close them before starting the sync.") - def start(self): - if len(self.config) > 0 and not self.reset: - die("Cannot start sync. Previous sync config found at %s\n" - "If you want to start submitting again from scratch " - "maybe you want to call git-p4 submit --reset" % self.configFile) - - commits = [] - for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): - commits.append(line.strip()) - commits.reverse() - - self.config["commits"] = commits - # replaces everything between 'Description:' and the next P4 submit template field with the # commit message def prepareLogMessage(self, template, message): @@ -723,42 +705,29 @@ class P4Submit(Command): print "Syncronizing p4 checkout..." system("p4 sync ...") - if self.reset: - self.firstTime = True - self.check() - self.configFile = self.gitdir + "/p4-git-sync.cfg" - self.config = shelve.open(self.configFile, writeback=True) - if self.firstTime: - self.start() - - commits = self.config.get("commits", []) + commits = [] + for line in read_pipe_lines("git rev-list --no-merges %s..%s" % (self.origin, self.master)): + commits.append(line.strip()) + commits.reverse() while len(commits) > 0: - self.firstTime = False commit = commits[0] commits = commits[1:] - self.config["commits"] = commits self.applyCommit(commit) if not self.interactive: break - self.config.close() - if len(commits) == 0: - if self.firstTime: - print "No changes found to apply between %s and current HEAD" % self.origin - else: - print "All changes applied!" - os.chdir(self.oldWorkingDirectory) + print "All changes applied!" + os.chdir(self.oldWorkingDirectory) - sync = P4Sync() - sync.run([]) + sync = P4Sync() + sync.run([]) - rebase = P4Rebase() - rebase.rebase() - os.remove(self.configFile) + rebase = P4Rebase() + rebase.rebase() return True -- cgit v1.2.3 From 3a70cdfa42199e16d2d047c286431c4274d65b1a Mon Sep 17 00:00:00 2001 From: Tor Arvid Lund Date: Mon, 18 Feb 2008 15:22:08 +0100 Subject: git-p4: Support usage of perforce client spec When syncing, git-p4 will only download files that are included in the active perforce client spec. This does not change the default behaviour - it requires that the user either supplies the command line argument --use-client-spec, or sets the git config option p4.useclientspec to "true". Signed-off-by: Tor Arvid Lund Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 50 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d59ed945de..be96600753 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -745,7 +745,9 @@ class P4Sync(Command): help="Import into refs/heads/ , not refs/remotes"), optparse.make_option("--max-changes", dest="maxChanges"), optparse.make_option("--keep-path", dest="keepRepoPath", action='store_true', - help="Keep entire BRANCH/DIR/SUBDIR prefix during import") + help="Keep entire BRANCH/DIR/SUBDIR prefix during import"), + optparse.make_option("--use-client-spec", dest="useClientSpec", action='store_true', + help="Only sync files that are included in the Perforce Client Spec") ] self.description = """Imports from Perforce into a git repository.\n example: @@ -772,6 +774,8 @@ class P4Sync(Command): self.depotPaths = None self.p4BranchesInGit = [] self.cloneExclude = [] + self.useClientSpec = False + self.clientSpecDirs = [] if gitConfig("git-p4.syncFromOrigin") == "false": self.syncWithOrigin = False @@ -846,11 +850,21 @@ class P4Sync(Command): ## Should move this out, doesn't use SELF. def readP4Files(self, files): + for f in files: + for val in self.clientSpecDirs: + if f['path'].startswith(val[0]): + if val[1] > 0: + f['include'] = True + else: + f['include'] = False + break + files = [f for f in files - if f['action'] != 'delete'] + if f['action'] != 'delete' and + (f.has_key('include') == False or f['include'] == True)] if not files: - return + return [] filedata = p4CmdList('-x - print', stdin='\n'.join(['%s#%s' % (f['path'], f['rev']) @@ -885,6 +899,7 @@ class P4Sync(Command): for f in files: assert not f.has_key('data') f['data'] = contents[f['path']] + return files def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] @@ -901,11 +916,7 @@ class P4Sync(Command): new_files.append (f) else: sys.stderr.write("Ignoring file outside of prefix: %s\n" % path) - files = new_files - self.readP4Files(files) - - - + files = self.readP4Files(new_files) self.gitStream.write("commit %s\n" % branch) # gitStream.write("mark :%s\n" % details["change"]) @@ -1320,6 +1331,26 @@ class P4Sync(Command): print self.gitError.read() + def getClientSpec(self): + specList = p4CmdList( "client -o" ) + temp = {} + for entry in specList: + for k,v in entry.iteritems(): + if k.startswith("View"): + if v.startswith('"'): + start = 1 + else: + start = 0 + index = v.find("...") + v = v[start:index] + if v.startswith("-"): + v = v[1:] + temp[v] = -len(v) + else: + temp[v] = len(v) + self.clientSpecDirs = temp.items() + self.clientSpecDirs.sort( lambda x, y: abs( y[1] ) - abs( x[1] ) ) + def run(self, args): self.depotPaths = [] self.changeRange = "" @@ -1352,6 +1383,9 @@ class P4Sync(Command): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch): system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) + if self.useClientSpec or gitConfig("p4.useclientspec") == "true": + self.getClientSpec() + # TODO: should always look at previous commits, # merge with previous imports, if possible. if args == []: -- cgit v1.2.3 From 21a2d69b2a14f1b42b18cfd18ff0477bb97eb11f Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 22 Feb 2008 16:48:53 +0100 Subject: git.el: Do not display empty directories. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexandre Julliard Tested-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index f69b697f8d..cc21e9c682 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -728,7 +728,7 @@ Return the list of files that haven't been handled." (defun git-run-ls-files-with-excludes (status files default-state &rest options) "Run git-ls-files on FILES with appropriate --exclude-from options." (let ((exclude-files (git-get-exclude-files))) - (apply #'git-run-ls-files status files default-state "--directory" + (apply #'git-run-ls-files status files default-state "--directory" "--no-empty-directory" (concat "--exclude-per-directory=" git-per-dir-ignore-file) (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) -- cgit v1.2.3 From be5f5bf02706357794cdf093ebe603aa82ae52eb Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 21 Feb 2008 16:21:49 +0000 Subject: completion: support format-patch's --cover-letter option Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8722a68795..8f70e1efc1 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -648,6 +648,7 @@ _git_format_patch () --in-reply-to= --full-index --binary --not --all + --cover-letter " return ;; -- cgit v1.2.3 From a1eebfb3a90b6c240afd1a32cfebe6ee5dbd72c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vanicat?= Date: Fri, 29 Feb 2008 19:28:19 +0100 Subject: git.el: find the git-status buffer whatever its name is MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-status used the buffer name to find git-status buffers, and that can fail if the buffer has another name, for example when multiple working directories is tracked. Signed-off-by: Rémi Vanicat Acked-by: Alexandre Julliard Tested-by: Xavier Maillard Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d8a06381f4..0312d891fd 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1432,7 +1432,7 @@ Commands: (with-current-buffer buffer (when (and list-buffers-directory (string-equal fulldir (expand-file-name list-buffers-directory)) - (string-match "\\*git-status\\*$" (buffer-name buffer))) + (eq major-mode 'git-status-mode)) (setq found buffer)))) (setq list (cdr list))) found)) -- cgit v1.2.3 From 5f4347bba39ddb147b06913ac263fc46954d2d0b Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 28 Feb 2008 00:25:20 -0500 Subject: add storage size output to 'git verify-pack -v' This can possibly break external scripts that depend on the previous output, but those script can't possibly be critical to Git usage, and fixing them should be trivial. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- contrib/stats/packinfo.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/stats/packinfo.pl b/contrib/stats/packinfo.pl index aab501ea08..f4a7b62cd9 100755 --- a/contrib/stats/packinfo.pl +++ b/contrib/stats/packinfo.pl @@ -93,7 +93,7 @@ my %depths; my @depths; while () { - my ($sha1, $type, $size, $offset, $depth, $parent) = split(/\s+/, $_); + my ($sha1, $type, $size, $space, $offset, $depth, $parent) = split(/\s+/, $_); next unless ($sha1 =~ /^[0-9a-f]{40}$/); $depths{$sha1} = $depth || 0; push(@depths, $depth || 0); -- cgit v1.2.3 From 211c89682eeef310f39022b91e88d07cd5784953 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 29 Feb 2008 01:45:45 +0000 Subject: Make git-remote a builtin Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/examples/git-remote.perl | 477 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 477 insertions(+) create mode 100755 contrib/examples/git-remote.perl (limited to 'contrib') diff --git a/contrib/examples/git-remote.perl b/contrib/examples/git-remote.perl new file mode 100755 index 0000000000..5cd69513cf --- /dev/null +++ b/contrib/examples/git-remote.perl @@ -0,0 +1,477 @@ +#!/usr/bin/perl -w + +use strict; +use Git; +my $git = Git->repository(); + +sub add_remote_config { + my ($hash, $name, $what, $value) = @_; + if ($what eq 'url') { + if (exists $hash->{$name}{'URL'}) { + print STDERR "Warning: more than one remote.$name.url\n"; + } + $hash->{$name}{'URL'} = $value; + } + elsif ($what eq 'fetch') { + $hash->{$name}{'FETCH'} ||= []; + push @{$hash->{$name}{'FETCH'}}, $value; + } + elsif ($what eq 'push') { + $hash->{$name}{'PUSH'} ||= []; + push @{$hash->{$name}{'PUSH'}}, $value; + } + if (!exists $hash->{$name}{'SOURCE'}) { + $hash->{$name}{'SOURCE'} = 'config'; + } +} + +sub add_remote_remotes { + my ($hash, $file, $name) = @_; + + if (exists $hash->{$name}) { + $hash->{$name}{'WARNING'} = 'ignored due to config'; + return; + } + + my $fh; + if (!open($fh, '<', $file)) { + print STDERR "Warning: cannot open $file\n"; + return; + } + my $it = { 'SOURCE' => 'remotes' }; + $hash->{$name} = $it; + while (<$fh>) { + chomp; + if (/^URL:\s*(.*)$/) { + # Having more than one is Ok -- it is used for push. + if (! exists $it->{'URL'}) { + $it->{'URL'} = $1; + } + } + elsif (/^Push:\s*(.*)$/) { + $it->{'PUSH'} ||= []; + push @{$it->{'PUSH'}}, $1; + } + elsif (/^Pull:\s*(.*)$/) { + $it->{'FETCH'} ||= []; + push @{$it->{'FETCH'}}, $1; + } + elsif (/^\#/) { + ; # ignore + } + else { + print STDERR "Warning: funny line in $file: $_\n"; + } + } + close($fh); +} + +sub list_remote { + my ($git) = @_; + my %seen = (); + my @remotes = eval { + $git->command(qw(config --get-regexp), '^remote\.'); + }; + for (@remotes) { + if (/^remote\.(\S+?)\.([^.\s]+)\s+(.*)$/) { + add_remote_config(\%seen, $1, $2, $3); + } + } + + my $dir = $git->repo_path() . "/remotes"; + if (opendir(my $dh, $dir)) { + local $_; + while ($_ = readdir($dh)) { + chomp; + next if (! -f "$dir/$_" || ! -r _); + add_remote_remotes(\%seen, "$dir/$_", $_); + } + } + + return \%seen; +} + +sub add_branch_config { + my ($hash, $name, $what, $value) = @_; + if ($what eq 'remote') { + if (exists $hash->{$name}{'REMOTE'}) { + print STDERR "Warning: more than one branch.$name.remote\n"; + } + $hash->{$name}{'REMOTE'} = $value; + } + elsif ($what eq 'merge') { + $hash->{$name}{'MERGE'} ||= []; + push @{$hash->{$name}{'MERGE'}}, $value; + } +} + +sub list_branch { + my ($git) = @_; + my %seen = (); + my @branches = eval { + $git->command(qw(config --get-regexp), '^branch\.'); + }; + for (@branches) { + if (/^branch\.([^.]*)\.(\S*)\s+(.*)$/) { + add_branch_config(\%seen, $1, $2, $3); + } + } + + return \%seen; +} + +my $remote = list_remote($git); +my $branch = list_branch($git); + +sub update_ls_remote { + my ($harder, $info) = @_; + + return if (($harder == 0) || + (($harder == 1) && exists $info->{'LS_REMOTE'})); + + my @ref = map { + s|^[0-9a-f]{40}\s+refs/heads/||; + $_; + } $git->command(qw(ls-remote --heads), $info->{'URL'}); + $info->{'LS_REMOTE'} = \@ref; +} + +sub list_wildcard_mapping { + my ($forced, $ours, $ls) = @_; + my %refs; + for (@$ls) { + $refs{$_} = 01; # bit #0 to say "they have" + } + for ($git->command('for-each-ref', "refs/remotes/$ours")) { + chomp; + next unless (s|^[0-9a-f]{40}\s[a-z]+\srefs/remotes/$ours/||); + next if ($_ eq 'HEAD'); + $refs{$_} ||= 0; + $refs{$_} |= 02; # bit #1 to say "we have" + } + my (@new, @stale, @tracked); + for (sort keys %refs) { + my $have = $refs{$_}; + if ($have == 1) { + push @new, $_; + } + elsif ($have == 2) { + push @stale, $_; + } + elsif ($have == 3) { + push @tracked, $_; + } + } + return \@new, \@stale, \@tracked; +} + +sub list_mapping { + my ($name, $info) = @_; + my $fetch = $info->{'FETCH'}; + my $ls = $info->{'LS_REMOTE'}; + my (@new, @stale, @tracked); + + for (@$fetch) { + next unless (/(\+)?([^:]+):(.*)/); + my ($forced, $theirs, $ours) = ($1, $2, $3); + if ($theirs eq 'refs/heads/*' && + $ours =~ /^refs\/remotes\/(.*)\/\*$/) { + # wildcard mapping + my ($w_new, $w_stale, $w_tracked) + = list_wildcard_mapping($forced, $1, $ls); + push @new, @$w_new; + push @stale, @$w_stale; + push @tracked, @$w_tracked; + } + elsif ($theirs =~ /\*/ || $ours =~ /\*/) { + print STDERR "Warning: unrecognized mapping in remotes.$name.fetch: $_\n"; + } + elsif ($theirs =~ s|^refs/heads/||) { + if (!grep { $_ eq $theirs } @$ls) { + push @stale, $theirs; + } + elsif ($ours ne '') { + push @tracked, $theirs; + } + } + } + return \@new, \@stale, \@tracked; +} + +sub show_mapping { + my ($name, $info) = @_; + my ($new, $stale, $tracked) = list_mapping($name, $info); + if (@$new) { + print " New remote branches (next fetch will store in remotes/$name)\n"; + print " @$new\n"; + } + if (@$stale) { + print " Stale tracking branches in remotes/$name (use 'git remote prune')\n"; + print " @$stale\n"; + } + if (@$tracked) { + print " Tracked remote branches\n"; + print " @$tracked\n"; + } +} + +sub prune_remote { + my ($name, $ls_remote) = @_; + if (!exists $remote->{$name}) { + print STDERR "No such remote $name\n"; + return 1; + } + my $info = $remote->{$name}; + update_ls_remote($ls_remote, $info); + + my ($new, $stale, $tracked) = list_mapping($name, $info); + my $prefix = "refs/remotes/$name"; + foreach my $to_prune (@$stale) { + my @v = $git->command(qw(rev-parse --verify), "$prefix/$to_prune"); + $git->command(qw(update-ref -d), "$prefix/$to_prune", $v[0]); + } + return 0; +} + +sub show_remote { + my ($name, $ls_remote) = @_; + if (!exists $remote->{$name}) { + print STDERR "No such remote $name\n"; + return 1; + } + my $info = $remote->{$name}; + update_ls_remote($ls_remote, $info); + + print "* remote $name\n"; + print " URL: $info->{'URL'}\n"; + for my $branchname (sort keys %$branch) { + next unless (defined $branch->{$branchname}{'REMOTE'} && + $branch->{$branchname}{'REMOTE'} eq $name); + my @merged = map { + s|^refs/heads/||; + $_; + } split(' ',"@{$branch->{$branchname}{'MERGE'}}"); + next unless (@merged); + print " Remote branch(es) merged with 'git pull' while on branch $branchname\n"; + print " @merged\n"; + } + if ($info->{'LS_REMOTE'}) { + show_mapping($name, $info); + } + if ($info->{'PUSH'}) { + my @pushed = map { + s|^refs/heads/||; + s|^\+refs/heads/|+|; + s|:refs/heads/|:|; + $_; + } @{$info->{'PUSH'}}; + print " Local branch(es) pushed with 'git push'\n"; + print " @pushed\n"; + } + return 0; +} + +sub add_remote { + my ($name, $url, $opts) = @_; + if (exists $remote->{$name}) { + print STDERR "remote $name already exists.\n"; + exit(1); + } + $git->command('config', "remote.$name.url", $url); + my $track = $opts->{'track'} || ["*"]; + + for (@$track) { + $git->command('config', '--add', "remote.$name.fetch", + $opts->{'mirror'} ? + "+refs/$_:refs/$_" : + "+refs/heads/$_:refs/remotes/$name/$_"); + } + if ($opts->{'fetch'}) { + $git->command('fetch', $name); + } + if (exists $opts->{'master'}) { + $git->command('symbolic-ref', "refs/remotes/$name/HEAD", + "refs/remotes/$name/$opts->{'master'}"); + } +} + +sub update_remote { + my ($name) = @_; + my @remotes; + + my $conf = $git->config("remotes." . $name); + if (defined($conf)) { + @remotes = split(' ', $conf); + } elsif ($name eq 'default') { + @remotes = (); + for (sort keys %$remote) { + my $do_fetch = $git->config_bool("remote." . $_ . + ".skipDefaultUpdate"); + unless ($do_fetch) { + push @remotes, $_; + } + } + } else { + print STDERR "Remote group $name does not exists.\n"; + exit(1); + } + for (@remotes) { + print "Updating $_\n"; + $git->command('fetch', "$_"); + } +} + +sub rm_remote { + my ($name) = @_; + if (!exists $remote->{$name}) { + print STDERR "No such remote $name\n"; + return 1; + } + + $git->command('config', '--remove-section', "remote.$name"); + + eval { + my @trackers = $git->command('config', '--get-regexp', + 'branch.*.remote', $name); + for (@trackers) { + /^branch\.(.*)?\.remote/; + $git->config('--unset', "branch.$1.remote"); + $git->config('--unset', "branch.$1.merge"); + } + }; + + my @refs = $git->command('for-each-ref', + '--format=%(refname) %(objectname)', "refs/remotes/$name"); + for (@refs) { + my ($ref, $object) = split; + $git->command(qw(update-ref -d), $ref, $object); + } + return 0; +} + +sub add_usage { + print STDERR "Usage: git remote add [-f] [-t track]* [-m master] \n"; + exit(1); +} + +my $VERBOSE = 0; +@ARGV = grep { + if ($_ eq '-v' or $_ eq '--verbose') { + $VERBOSE=1; + 0 + } else { + 1 + } +} @ARGV; + +if (!@ARGV) { + for (sort keys %$remote) { + print "$_"; + print "\t$remote->{$_}->{URL}" if $VERBOSE; + print "\n"; + } +} +elsif ($ARGV[0] eq 'show') { + my $ls_remote = 1; + my $i; + for ($i = 1; $i < @ARGV; $i++) { + if ($ARGV[$i] eq '-n') { + $ls_remote = 0; + } + else { + last; + } + } + if ($i >= @ARGV) { + print STDERR "Usage: git remote show \n"; + exit(1); + } + my $status = 0; + for (; $i < @ARGV; $i++) { + $status |= show_remote($ARGV[$i], $ls_remote); + } + exit($status); +} +elsif ($ARGV[0] eq 'update') { + if (@ARGV <= 1) { + update_remote("default"); + exit(1); + } + for (my $i = 1; $i < @ARGV; $i++) { + update_remote($ARGV[$i]); + } +} +elsif ($ARGV[0] eq 'prune') { + my $ls_remote = 1; + my $i; + for ($i = 1; $i < @ARGV; $i++) { + if ($ARGV[$i] eq '-n') { + $ls_remote = 0; + } + else { + last; + } + } + if ($i >= @ARGV) { + print STDERR "Usage: git remote prune \n"; + exit(1); + } + my $status = 0; + for (; $i < @ARGV; $i++) { + $status |= prune_remote($ARGV[$i], $ls_remote); + } + exit($status); +} +elsif ($ARGV[0] eq 'add') { + my %opts = (); + while (1 < @ARGV && $ARGV[1] =~ /^-/) { + my $opt = $ARGV[1]; + shift @ARGV; + if ($opt eq '-f' || $opt eq '--fetch') { + $opts{'fetch'} = 1; + next; + } + if ($opt eq '-t' || $opt eq '--track') { + if (@ARGV < 1) { + add_usage(); + } + $opts{'track'} ||= []; + push @{$opts{'track'}}, $ARGV[1]; + shift @ARGV; + next; + } + if ($opt eq '-m' || $opt eq '--master') { + if ((@ARGV < 1) || exists $opts{'master'}) { + add_usage(); + } + $opts{'master'} = $ARGV[1]; + shift @ARGV; + next; + } + if ($opt eq '--mirror') { + $opts{'mirror'} = 1; + next; + } + add_usage(); + } + if (@ARGV != 3) { + add_usage(); + } + add_remote($ARGV[1], $ARGV[2], \%opts); +} +elsif ($ARGV[0] eq 'rm') { + if (@ARGV <= 1) { + print STDERR "Usage: git remote rm \n"; + exit(1); + } + exit(rm_remote($ARGV[1])); +} +else { + print STDERR "Usage: git remote\n"; + print STDERR " git remote add \n"; + print STDERR " git remote rm \n"; + print STDERR " git remote show \n"; + print STDERR " git remote prune \n"; + print STDERR " git remote update [group]\n"; + exit(1); +} -- cgit v1.2.3 From 30b5940bcd3ed7392795f6a27563013f6f806de4 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Mon, 3 Mar 2008 11:55:48 +0100 Subject: git-p4: Fix import of changesets with file deletions Commit 3a70cdfa42199e16d2d047c286431c4274d65b1a made readP4Files abort quickly when the changeset only contains files that are marked for deletion with an empty return value, which caused the commit to not do anything. This commit changes readP4Files to distinguish between files that need to be passed to p4 print and files that have no content ("deleted") and merge them in the returned list. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index be96600753..650ea34176 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -850,29 +850,32 @@ class P4Sync(Command): ## Should move this out, doesn't use SELF. def readP4Files(self, files): + filesForCommit = [] + filesToRead = [] + for f in files: + includeFile = True for val in self.clientSpecDirs: if f['path'].startswith(val[0]): - if val[1] > 0: - f['include'] = True - else: - f['include'] = False + if val[1] <= 0: + includeFile = False break - files = [f for f in files - if f['action'] != 'delete' and - (f.has_key('include') == False or f['include'] == True)] + if includeFile: + filesForCommit.append(f) + if f['action'] != 'delete': + filesToRead.append(f) - if not files: - return [] + filedata = [] + if len(filesToRead) > 0: + filedata = p4CmdList('-x - print', + stdin='\n'.join(['%s#%s' % (f['path'], f['rev']) + for f in filesToRead]), + stdin_mode='w+') - filedata = p4CmdList('-x - print', - stdin='\n'.join(['%s#%s' % (f['path'], f['rev']) - for f in files]), - stdin_mode='w+') - if "p4ExitCode" in filedata[0]: - die("Problems executing p4. Error: [%d]." - % (filedata[0]['p4ExitCode'])); + if "p4ExitCode" in filedata[0]: + die("Problems executing p4. Error: [%d]." + % (filedata[0]['p4ExitCode'])); j = 0; contents = {} @@ -896,10 +899,12 @@ class P4Sync(Command): contents[stat['depotFile']] = text - for f in files: - assert not f.has_key('data') - f['data'] = contents[f['path']] - return files + for f in filesForCommit: + path = f['path'] + if contents.has_key(path): + f['data'] = contents[path] + + return filesForCommit def commit(self, details, files, branch, branchPrefixes, parent = ""): epoch = details["time"] -- cgit v1.2.3 From 3041c324305e2bad59d7372336940846646dd46a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 4 Mar 2008 00:25:06 -0800 Subject: am: --rebasing The new option --rebasing is used internally for rebase to tell am that it is being used for its purpose. This would leave .dotest/rebasing to help "completion" scripts tell if the ongoing operation is am or rebase. Also the option at the same time stands for --binary, -3 and -k which are always given when rebase drives am as its backend. Using the information "am" leaves, git-completion.bash tells ongoing rebase and am apart. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8f70e1efc1..5ae87998e6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -70,7 +70,15 @@ __git_ps1 () local b if [ -d "$g/../.dotest" ] then - r="|AM/REBASE" + if test -f "$g/../.dotest/rebasing" + then + r="|REBASE" + elif test -f "$g/../.dotest/applying" + then + r="|AM" + else + r="|AM/REBASE" + fi b="$(git symbolic-ref HEAD 2>/dev/null)" elif [ -f "$g/.dotest-merge/interactive" ] then -- cgit v1.2.3 From 9225d7be0abab0c3b1325a733aea127b45a18625 Mon Sep 17 00:00:00 2001 From: Denis Cheng Date: Sun, 2 Mar 2008 17:05:52 +0800 Subject: specify explicit "--pretty=medium" with `git log/show/whatchanged` The following patch will introduce a new configuration variable, "format.pretty", from then on the pretty format without specifying "--pretty" might not be the default "--pretty=medium", it depends on the user's config. So all kinds of Shell/Perl/Emacs scripts that needs the default medium pretty format must specify it explicitly. Signed-off-by: Denis Cheng Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 2 +- contrib/hooks/post-receive-email | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index c9268234a5..4fa853fae7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1299,7 +1299,7 @@ Return the list of files that haven't been handled." (let (author-name author-email subject date msg) (with-temp-buffer (let ((coding-system (git-get-logoutput-coding-system))) - (git-call-process-env t nil "log" "-1" commit) + (git-call-process-env t nil "log" "-1" "--pretty=medium" commit) (goto-char (point-min)) (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t) (setq author-name (match-string 1)) diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 77c88ebf1f..62a740c482 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -567,7 +567,7 @@ generate_general_email() echo "" if [ "$newrev_type" = "commit" ]; then echo $LOGBEGIN - git show --no-color --root -s $newrev + git show --no-color --root -s --pretty=medium $newrev echo $LOGEND else # What can we do here? The tag marks an object that is not -- cgit v1.2.3 From 3b376b0cb8b5d6b7b0d3f3acff6a31f350a7ab04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Tue, 4 Mar 2008 19:00:58 +0100 Subject: bash: add git-branch options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8f70e1efc1..8d6733abe4 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -506,7 +506,16 @@ _git_bisect () _git_branch () { - __gitcomp "$(__git_refs)" + case "${COMP_WORDS[COMP_CWORD]}" in + --*=*) COMPREPLY=() ;; + --*) + __gitcomp " + --color --no-color --verbose --abbrev= --no-abbrev + --track --no-track + " + ;; + *) __gitcomp "$(__git_refs)" ;; + esac } _git_bundle () -- cgit v1.2.3 From b9217642ef2db34e2cbeaef8d4439b07a03027cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Tue, 4 Mar 2008 19:00:59 +0100 Subject: bash: git-branch -d and -m lists only local branches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit But still all branches are listed, if -r is present Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8d6733abe4..49e6df0965 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -506,6 +506,17 @@ _git_bisect () _git_branch () { + local i c=1 only_local_ref="n" has_r="n" + + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + -d|-m) only_local_ref="y" ;; + -r) has_r="y" ;; + esac + c=$((++c)) + done + case "${COMP_WORDS[COMP_CWORD]}" in --*=*) COMPREPLY=() ;; --*) @@ -514,7 +525,13 @@ _git_branch () --track --no-track " ;; - *) __gitcomp "$(__git_refs)" ;; + *) + if [ $only_local_ref = "y" -a $has_r = "n" ]; then + __gitcomp "$(__git_heads)" + else + __gitcomp "$(__git_refs)" + fi + ;; esac } -- cgit v1.2.3 From a5c4f85b1685aeb01f4dac88075f465c71a6c82d Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sat, 8 Mar 2008 22:10:48 -0500 Subject: bash: Properly quote the GIT_DIR at all times to fix subdirectory paths with spaces Signed-off-by: Kevin Ballard Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 848c067b57..c29569c624 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -83,17 +83,17 @@ __git_ps1 () elif [ -f "$g/.dotest-merge/interactive" ] then r="|REBASE-i" - b="$(cat $g/.dotest-merge/head-name)" + b="$(cat "$g/.dotest-merge/head-name")" elif [ -d "$g/.dotest-merge" ] then r="|REBASE-m" - b="$(cat $g/.dotest-merge/head-name)" + b="$(cat "$g/.dotest-merge/head-name")" elif [ -f "$g/MERGE_HEAD" ] then r="|MERGING" b="$(git symbolic-ref HEAD 2>/dev/null)" else - if [ -f $g/BISECT_LOG ] + if [ -f "$g/BISECT_LOG" ] then r="|BISECTING" fi @@ -101,7 +101,7 @@ __git_ps1 () then if ! b="$(git describe --exact-match HEAD 2>/dev/null)" then - b="$(cut -c1-7 $g/HEAD)..." + b="$(cut -c1-7 "$g/HEAD")..." fi fi fi -- cgit v1.2.3 From 1d17b22ebf9d894cef393e3e062f383aad8817e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 10 Mar 2008 16:02:22 +0100 Subject: bash: remove unnecessary conditions when checking for subcommands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Checking emptyness of $command is sufficient. Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c29569c624..a94dc88fdc 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -497,7 +497,7 @@ _git_bisect () c=$((++c)) done - if [ $c -eq $COMP_CWORD -a -z "$command" ]; then + if [ -z "$command" ]; then __gitcomp "start bad good reset visualize replay log" return fi @@ -1042,7 +1042,7 @@ _git_remote () c=$((++c)) done - if [ $c -eq $COMP_CWORD -a -z "$command" ]; then + if [ -z "$command" ]; then __gitcomp "add rm show prune update" return fi @@ -1135,7 +1135,7 @@ _git_submodule () c=$((++c)) done - if [ $c -eq $COMP_CWORD -a -z "$command" ]; then + if [ -z "$command" ]; then local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -1198,7 +1198,7 @@ _git () c=$((++c)) done - if [ $c -eq $COMP_CWORD -a -z "$command" ]; then + if [ -z "$command" ]; then case "${COMP_WORDS[COMP_CWORD]}" in --*=*) COMPREPLY=() ;; --*) __gitcomp " -- cgit v1.2.3 From 3ff1320d4be164e35a685d8d00b6f44084be76e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 10 Mar 2008 16:02:23 +0100 Subject: bash: refactor searching for subcommands on the command line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the __git_find_subcommand function, which takes one argument: a string containing all subcommands separated by spaces. The function searches through the command line whether a subcommand is already present. The first found subcommand will be printed to standard output. This enables us to remove code duplications from completion functions for commands having subcommands. Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 71 +++++++++++++++------------------- 1 file changed, 32 insertions(+), 39 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a94dc88fdc..438e193bd0 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -428,6 +428,22 @@ __git_aliased_command () done } +__git_find_subcommand () +{ + local word subcommand c=1 + + while [ $c -lt $COMP_CWORD ]; do + word="${COMP_WORDS[c]}" + for subcommand in $1; do + if [ "$subcommand" = "$word" ]; then + echo "$subcommand" + return + fi + done + c=$((++c)) + done +} + __git_whitespacelist="nowarn warn error error-all strip" _git_am () @@ -485,24 +501,14 @@ _git_add () _git_bisect () { - local i c=1 command - while [ $c -lt $COMP_CWORD ]; do - i="${COMP_WORDS[c]}" - case "$i" in - start|bad|good|reset|visualize|replay|log) - command="$i" - break - ;; - esac - c=$((++c)) - done - - if [ -z "$command" ]; then - __gitcomp "start bad good reset visualize replay log" + local subcommands="start bad good reset visualize replay log" + local subcommand="$(__git_find_subcommand "$subcommands")" + if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" return fi - case "$command" in + case "$subcommand" in bad|good|reset) __gitcomp "$(__git_refs)" ;; @@ -1033,21 +1039,13 @@ _git_config () _git_remote () { - local i c=1 command - while [ $c -lt $COMP_CWORD ]; do - i="${COMP_WORDS[c]}" - case "$i" in - add|rm|show|prune|update) command="$i"; break ;; - esac - c=$((++c)) - done - - if [ -z "$command" ]; then - __gitcomp "add rm show prune update" + local subcommands="add rm show prune update" + local subcommand="$(__git_find_subcommand "$subcommands")" + if [ -z "$subcommand" ]; then return fi - case "$command" in + case "$subcommand" in rm|show|prune) __gitcomp "$(__git_remotes)" ;; @@ -1121,28 +1119,23 @@ _git_show () _git_stash () { - __gitcomp 'list show apply clear' + local subcommands='list show apply clear' + if [ -z "$(__git_find_subcommand "$subcommands")" ]; then + __gitcomp "$subcommands" + fi } _git_submodule () { - local i c=1 command - while [ $c -lt $COMP_CWORD ]; do - i="${COMP_WORDS[c]}" - case "$i" in - add|status|init|update) command="$i"; break ;; - esac - c=$((++c)) - done - - if [ -z "$command" ]; then + local subcommands="add status init update" + if [ -z "$(__git_find_subcommand "$subcommands")" ]; then local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) __gitcomp "--quiet --cached" ;; *) - __gitcomp "add status init update" + __gitcomp "$subcommands" ;; esac return -- cgit v1.2.3 From 88b302f5e2e0a6f6610912df49c473967fe59c89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 10 Mar 2008 16:02:24 +0100 Subject: bash: add new 'git stash' subcommands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Namely 'save', 'drop', 'pop' and 'create' Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 438e193bd0..c62b877aeb 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1119,7 +1119,7 @@ _git_show () _git_stash () { - local subcommands='list show apply clear' + local subcommands='save list show apply clear drop pop create' if [ -z "$(__git_find_subcommand "$subcommands")" ]; then __gitcomp "$subcommands" fi -- cgit v1.2.3 From 47f6ee283891b757069c82922a88d1393be25dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 10 Mar 2008 16:02:25 +0100 Subject: bash: add 'git svn' subcommands and options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 81 +++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c62b877aeb..2c4bf4e1d2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -384,7 +384,6 @@ __git_commands () show-index) : plumbing;; ssh-*) : transport;; stripspace) : plumbing;; - svn) : import export;; symbolic-ref) : plumbing;; tar-tree) : deprecated;; unpack-file) : plumbing;; @@ -1142,6 +1141,84 @@ _git_submodule () fi } +_git_svn () +{ + local subcommands=" + init fetch clone rebase dcommit log find-rev + set-tree commit-diff info create-ignore propget + proplist show-ignore show-externals + " + local subcommand="$(__git_find_subcommand "$subcommands")" + if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" + else + local remote_opts="--username= --config-dir= --no-auth-cache" + local fc_opts=" + --follow-parent --authors-file= --repack= + --no-metadata --use-svm-props --use-svnsync-props + --log-window-size= --no-checkout --quiet + --repack-flags --user-log-author $remote_opts + " + local init_opts=" + --template= --shared= --trunk= --tags= + --branches= --stdlayout --minimize-url + --no-metadata --use-svm-props --use-svnsync-props + --rewrite-root= $remote_opts + " + local cmt_opts=" + --edit --rmdir --find-copies-harder --copy-similarity= + " + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$subcommand,$cur" in + fetch,--*) + __gitcomp "--revision= --fetch-all $fc_opts" + ;; + clone,--*) + __gitcomp "--revision= $fc_opts $init_opts" + ;; + init,--*) + __gitcomp "$init_opts" + ;; + dcommit,--*) + __gitcomp " + --merge --strategy= --verbose --dry-run + --fetch-all --no-rebase $cmt_opts $fc_opts + " + ;; + set-tree,--*) + __gitcomp "--stdin $cmt_opts $fc_opts" + ;; + create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\ + show-externals,--*) + __gitcomp "--revision=" + ;; + log,--*) + __gitcomp " + --limit= --revision= --verbose --incremental + --oneline --show-commit --non-recursive + --authors-file= + " + ;; + rebase,--*) + __gitcomp " + --merge --verbose --strategy= --local + --fetch-all $fc_opts + " + ;; + commit-diff,--*) + __gitcomp "--message= --file= --revision= $cmt_opts" + ;; + info,--*) + __gitcomp "--url" + ;; + *) + COMPREPLY=() + ;; + esac + fi +} + _git_tag () { local i c=1 f=0 @@ -1243,6 +1320,7 @@ _git () show-branch) _git_log ;; stash) _git_stash ;; submodule) _git_submodule ;; + svn) _git_svn ;; tag) _git_tag ;; whatchanged) _git_log ;; *) COMPREPLY=() ;; @@ -1293,6 +1371,7 @@ complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_stash git-stash complete -o default -o nospace -F _git_submodule git-submodule +complete -o default -o nospace -F _git_svn git-svn complete -o default -o nospace -F _git_log git-show-branch complete -o default -o nospace -F _git_tag git-tag complete -o default -o nospace -F _git_log git-whatchanged -- cgit v1.2.3 From 6753f2aa55280a0fef1cbdcee71c1b529cb0c910 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 10 Mar 2008 19:49:19 -0400 Subject: bash: Remove completion of core.legacyheaders option This option is no longer recognized by git. Completing it is not worthwhile. Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 1 - 1 file changed, 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2c4bf4e1d2..73ed095487 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -961,7 +961,6 @@ _git_config () core.sharedRepository core.warnAmbiguousRefs core.compression - core.legacyHeaders core.packedGitWindowSize core.packedGitLimit clean.requireForce -- cgit v1.2.3 From 51fe120903370ca8bf384c839c8cfb70ee563eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Thu, 6 Mar 2008 22:37:36 +0100 Subject: bash: use __gitdir when completing 'git rebase' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When doing completion of rebase options in a subdirectory of the work tree during an ongoing rebase, wrong options were offered because of the hardcoded .git/.dotest-merge path. Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 73ed095487..fc108e4828 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -841,8 +841,8 @@ _git_push () _git_rebase () { - local cur="${COMP_WORDS[COMP_CWORD]}" - if [ -d .dotest ] || [ -d .git/.dotest-merge ]; then + local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" + if [ -d .dotest ] || [ -d "$dir"/.dotest-merge ]; then __gitcomp "--continue --skip --abort" return fi -- cgit v1.2.3 From ce5a2c956f10f84d8403fc4e94106a6b33a5024a Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Thu, 6 Mar 2008 18:52:37 +0200 Subject: bash: Add more long options to be completed with "git --" Add the following long options to be completed with command "git": --paginate --work-tree= --help Signed-off-by: Teemu Likonen Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index fc108e4828..2d11d0a97f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1271,11 +1271,14 @@ _git () case "${COMP_WORDS[COMP_CWORD]}" in --*=*) COMPREPLY=() ;; --*) __gitcomp " + --paginate --no-pager --git-dir= --bare --version --exec-path + --work-tree= + --help " ;; *) __gitcomp "$(__git_commands) $(__git_aliases)" ;; -- cgit v1.2.3 From 5447aac755e5cfb948c627762b4665801811b026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Wed, 5 Mar 2008 20:07:49 +0100 Subject: bash: fix long option with argument double completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pressing TAB right after 'git command --long-option=' results in 'git command --long-option=--long-option=' when the long option requires an argument, but we don't provide completion for its arguments (e.g. commit --author=, apply --exclude=). This patch detects these long options and provides empty completion array for them. Signed-off-by: SZEDER Gábor Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2d11d0a97f..5046f69934 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -121,13 +121,21 @@ __gitcomp () if [ $# -gt 2 ]; then cur="$3" fi - for c in $1; do - case "$c$4" in - --*=*) all="$all$c$4$s" ;; - *.) all="$all$c$4$s" ;; - *) all="$all$c$4 $s" ;; - esac - done + case "$cur" in + --*=) + COMPREPLY=() + return + ;; + *) + for c in $1; do + case "$c$4" in + --*=*) all="$all$c$4$s" ;; + *.) all="$all$c$4$s" ;; + *) all="$all$c$4 $s" ;; + esac + done + ;; + esac IFS=$s COMPREPLY=($(compgen -P "$2" -W "$all" -- "$cur")) return -- cgit v1.2.3 From 8ff45f2af5b25b7581072ee7896f4285dfc034ea Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Mon, 3 Mar 2008 13:42:47 +0100 Subject: git-p4: Optimize the fetching of data from perforce. Use shallow copies in loop, and join content at the end. Then do the substitution, if needed. Signed-off-by: Marius Storm-Olsen Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 650ea34176..539c5cda07 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -882,21 +882,21 @@ class P4Sync(Command): while j < len(filedata): stat = filedata[j] j += 1 - text = '' + text = []; while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'): - tmp = filedata[j]['data'] - if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): - tmp = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', tmp) - elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): - tmp = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', tmp) - text += tmp + text.append(filedata[j]['data']) j += 1 - + text = ''.join(text) if not stat.has_key('depotFile'): sys.stderr.write("p4 print fails with: %s\n" % repr(stat)) continue + if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): + text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text) + elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): + text = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text) + contents[stat['depotFile']] = text for f in filesForCommit: -- cgit v1.2.3 From 67abd417165d1e7716d947949f5e5e27318c8a29 Mon Sep 17 00:00:00 2001 From: Shawn Bohrer Date: Wed, 12 Mar 2008 19:03:23 -0500 Subject: git-p4: Unset P4DIFF environment variable when using 'p4 -du diff' A custom diffing utility can be specified for the 'p4 diff' command by setting the P4DIFF environment variable. However when using a custom diffing utility such as 'vimdiff' passing options like -du can cause unexpected behavior. Since the goal is to generate a unified diff of the changes and attach them to the bottom of the p4 submit log we should unset P4DIFF if it has been set in order to generate the diff properly. Signed-off-by: Shawn Bohrer Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 539c5cda07..28b9c3c3cb 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -627,6 +627,8 @@ class P4Submit(Command): if self.interactive: submitTemplate = self.prepareLogMessage(template, logMessage) + if os.environ.has_key("P4DIFF"): + del(os.environ["P4DIFF"]) diff = read_pipe("p4 diff -du ...") for newFile in filesToAdd: -- cgit v1.2.3 From 82cea9ffb1c4677155e3e2996d76542502611370 Mon Sep 17 00:00:00 2001 From: Shawn Bohrer Date: Wed, 12 Mar 2008 19:03:24 -0500 Subject: git-p4: Use P4EDITOR environment variable when set Perforce allows you to set the P4EDITOR environment variable to your preferred editor for use in perforce. Since we are displaying a perforce changelog to the user we should use it when it is defined. Signed-off-by: Shawn Bohrer Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 28b9c3c3cb..3cb0330ec2 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -652,7 +652,10 @@ class P4Submit(Command): defaultEditor = "vi" if platform.system() == "Windows": defaultEditor = "notepad" - editor = os.environ.get("EDITOR", defaultEditor); + if os.environ.has_key("P4EDITOR"): + editor = os.environ.get("P4EDITOR") + else: + editor = os.environ.get("EDITOR", defaultEditor); system(editor + " " + fileName) tmpFile = open(fileName, "rb") message = tmpFile.read() -- cgit v1.2.3 From ac378633f3cae46d2c88255e53ee38a6206d3777 Mon Sep 17 00:00:00 2001 From: Bernt Hansen Date: Fri, 14 Mar 2008 22:44:13 -0400 Subject: git-new-workdir: Share SVN meta data between work dirs and the repository Multiple work dirs with git svn caused each work dir to have its own stale copy of the SVN meta data in .git/svn git svn rebase updates commits with git-svn-id: in the repository and stores the SVN meta data information only in that work dir. Attempting to git svn rebase in other work dirs for the same branch would fail because the last revision fetched according to the git-svn-id is greater than the revision in the SVN meta data for that work directory. Signed-off-by: Bernt Hansen Acked-by: Eric Wong Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 2838546d16..7959eab902 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -63,7 +63,7 @@ mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!" # create the links to the original repo. explictly exclude index, HEAD and # logs/HEAD from the list since they are purely related to the current working # directory, and should not be shared. -for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache +for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn do case $x in */*) -- cgit v1.2.3 From c817faabd7b0d7c0779ad5b34a4411ba3cdccf2d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 15 Mar 2008 23:58:10 -0700 Subject: Resurrect git-rerere to contrib/examples It is handy to have a copy readily available for checking regressions. Signed-off-by: Junio C Hamano --- contrib/examples/git-rerere.perl | 284 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100755 contrib/examples/git-rerere.perl (limited to 'contrib') diff --git a/contrib/examples/git-rerere.perl b/contrib/examples/git-rerere.perl new file mode 100755 index 0000000000..4f692091e7 --- /dev/null +++ b/contrib/examples/git-rerere.perl @@ -0,0 +1,284 @@ +#!/usr/bin/perl +# +# REuse REcorded REsolve. This tool records a conflicted automerge +# result and its hand resolution, and helps to resolve future +# automerge that results in the same conflict. +# +# To enable this feature, create a directory 'rr-cache' under your +# .git/ directory. + +use Digest; +use File::Path; +use File::Copy; + +my $git_dir = $::ENV{GIT_DIR} || ".git"; +my $rr_dir = "$git_dir/rr-cache"; +my $merge_rr = "$git_dir/rr-cache/MERGE_RR"; + +my %merge_rr = (); + +sub read_rr { + if (!-f $merge_rr) { + %merge_rr = (); + return; + } + my $in; + local $/ = "\0"; + open $in, "<$merge_rr" or die "$!: $merge_rr"; + while (<$in>) { + chomp; + my ($name, $path) = /^([0-9a-f]{40})\t(.*)$/s; + $merge_rr{$path} = $name; + } + close $in; +} + +sub write_rr { + my $out; + open $out, ">$merge_rr" or die "$!: $merge_rr"; + for my $path (sort keys %merge_rr) { + my $name = $merge_rr{$path}; + print $out "$name\t$path\0"; + } + close $out; +} + +sub compute_conflict_name { + my ($path) = @_; + my @side = (); + my $in; + open $in, "<$path" or die "$!: $path"; + + my $sha1 = Digest->new("SHA-1"); + my $hunk = 0; + while (<$in>) { + if (/^<<<<<<< .*/) { + $hunk++; + @side = ([], undef); + } + elsif (/^=======$/) { + $side[1] = []; + } + elsif (/^>>>>>>> .*/) { + my ($one, $two); + $one = join('', @{$side[0]}); + $two = join('', @{$side[1]}); + if ($two le $one) { + ($one, $two) = ($two, $one); + } + $sha1->add($one); + $sha1->add("\0"); + $sha1->add($two); + $sha1->add("\0"); + @side = (); + } + elsif (@side == 0) { + next; + } + elsif (defined $side[1]) { + push @{$side[1]}, $_; + } + else { + push @{$side[0]}, $_; + } + } + close $in; + return ($sha1->hexdigest, $hunk); +} + +sub record_preimage { + my ($path, $name) = @_; + my @side = (); + my ($in, $out); + open $in, "<$path" or die "$!: $path"; + open $out, ">$name" or die "$!: $name"; + + while (<$in>) { + if (/^<<<<<<< .*/) { + @side = ([], undef); + } + elsif (/^=======$/) { + $side[1] = []; + } + elsif (/^>>>>>>> .*/) { + my ($one, $two); + $one = join('', @{$side[0]}); + $two = join('', @{$side[1]}); + if ($two le $one) { + ($one, $two) = ($two, $one); + } + print $out "<<<<<<<\n"; + print $out $one; + print $out "=======\n"; + print $out $two; + print $out ">>>>>>>\n"; + @side = (); + } + elsif (@side == 0) { + print $out $_; + } + elsif (defined $side[1]) { + push @{$side[1]}, $_; + } + else { + push @{$side[0]}, $_; + } + } + close $out; + close $in; +} + +sub find_conflict { + my $in; + local $/ = "\0"; + my $pid = open($in, '-|'); + die "$!" unless defined $pid; + if (!$pid) { + exec(qw(git ls-files -z -u)) or die "$!: ls-files"; + } + my %path = (); + my @path = (); + while (<$in>) { + chomp; + my ($mode, $sha1, $stage, $path) = + /^([0-7]+) ([0-9a-f]{40}) ([123])\t(.*)$/s; + $path{$path} |= (1 << $stage); + } + close $in; + while (my ($path, $status) = each %path) { + if ($status == 14) { push @path, $path; } + } + return @path; +} + +sub merge { + my ($name, $path) = @_; + record_preimage($path, "$rr_dir/$name/thisimage"); + unless (system('git', 'merge-file', map { "$rr_dir/$name/${_}image" } + qw(this pre post))) { + my $in; + open $in, "<$rr_dir/$name/thisimage" or + die "$!: $name/thisimage"; + my $out; + open $out, ">$path" or die "$!: $path"; + while (<$in>) { print $out $_; } + close $in; + close $out; + return 1; + } + return 0; +} + +sub garbage_collect_rerere { + # We should allow specifying these from the command line and + # that is why the caller gives @ARGV to us, but I am lazy. + + my $cutoff_noresolve = 15; # two weeks + my $cutoff_resolve = 60; # two months + my @to_remove; + while (<$rr_dir/*/preimage>) { + my ($dir) = /^(.*)\/preimage$/; + my $cutoff = ((-f "$dir/postimage") + ? $cutoff_resolve + : $cutoff_noresolve); + my $age = -M "$_"; + if ($cutoff <= $age) { + push @to_remove, $dir; + } + } + if (@to_remove) { + rmtree(\@to_remove); + } +} + +-d "$rr_dir" || exit(0); + +read_rr(); + +if (@ARGV) { + my $arg = shift @ARGV; + if ($arg eq 'clear') { + for my $path (keys %merge_rr) { + my $name = $merge_rr{$path}; + if (-d "$rr_dir/$name" && + ! -f "$rr_dir/$name/postimage") { + rmtree(["$rr_dir/$name"]); + } + } + unlink $merge_rr; + } + elsif ($arg eq 'status') { + for my $path (keys %merge_rr) { + print $path, "\n"; + } + } + elsif ($arg eq 'diff') { + for my $path (keys %merge_rr) { + my $name = $merge_rr{$path}; + system('diff', ((@ARGV == 0) ? ('-u') : @ARGV), + '-L', "a/$path", '-L', "b/$path", + "$rr_dir/$name/preimage", $path); + } + } + elsif ($arg eq 'gc') { + garbage_collect_rerere(@ARGV); + } + else { + die "$0 unknown command: $arg\n"; + } + exit 0; +} + +my %conflict = map { $_ => 1 } find_conflict(); + +# MERGE_RR records paths with conflicts immediately after merge +# failed. Some of the conflicted paths might have been hand resolved +# in the working tree since then, but the initial run would catch all +# and register their preimages. + +for my $path (keys %conflict) { + # This path has conflict. If it is not recorded yet, + # record the pre-image. + if (!exists $merge_rr{$path}) { + my ($name, $hunk) = compute_conflict_name($path); + next unless ($hunk); + $merge_rr{$path} = $name; + if (! -d "$rr_dir/$name") { + mkpath("$rr_dir/$name", 0, 0777); + print STDERR "Recorded preimage for '$path'\n"; + record_preimage($path, "$rr_dir/$name/preimage"); + } + } +} + +# Now some of the paths that had conflicts earlier might have been +# hand resolved. Others may be similar to a conflict already that +# was resolved before. + +for my $path (keys %merge_rr) { + my $name = $merge_rr{$path}; + + # We could resolve this automatically if we have images. + if (-f "$rr_dir/$name/preimage" && + -f "$rr_dir/$name/postimage") { + if (merge($name, $path)) { + print STDERR "Resolved '$path' using previous resolution.\n"; + # Then we do not have to worry about this path + # anymore. + delete $merge_rr{$path}; + next; + } + } + + # Let's see if we have resolved it. + (undef, my $hunk) = compute_conflict_name($path); + next if ($hunk); + + print STDERR "Recorded resolution for '$path'.\n"; + copy($path, "$rr_dir/$name/postimage"); + # And we do not have to worry about this path anymore. + delete $merge_rr{$path}; +} + +# Write out the rest. +write_rr(); -- cgit v1.2.3 From f3e5ae4f06ae968b710d286280a46b38ae3d36e8 Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Fri, 28 Mar 2008 15:40:40 +0100 Subject: git-p4: Handle Windows EOLs properly after removal of p4 submit template handling. git-p4s handling of Windows style EOL was broken after the removal of the p4 submit template handling in commit f2a6059. Fix that, and make getP4OpenedType() more robust. Signed-off-by: Marius Storm-Olsen Signed-off-by: Simon Hausmann --- contrib/fast-import/git-p4 | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3cb0330ec2..d8de9f6c25 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -90,11 +90,11 @@ def getP4OpenedType(file): # Returns the perforce file type for the given file. result = read_pipe("p4 opened %s" % file) - match = re.match(".*\((.+)\)$", result) + match = re.match(".*\((.+)\)\r?$", result) if match: return match.group(1) else: - die("Could not determine file type for %s" % file) + die("Could not determine file type for %s (result: '%s')" % (file, result)) def diffTreePattern(): # This is a simple generator for the diff tree regex pattern. This could be @@ -513,6 +513,8 @@ class P4Submit(Command): template = "" inFilesSection = False for line in read_pipe_lines("p4 change -o"): + if line.endswith("\r\n"): + line = line[:-2] + "\n" if inFilesSection: if line.startswith("\t"): # path starts and ends with a tab @@ -619,8 +621,6 @@ class P4Submit(Command): setP4ExecBit(f, mode) logMessage = extractLogMessageFromGitCommit(id) - if self.isWindows: - logMessage = logMessage.replace("\n", "\r\n") logMessage = logMessage.strip() template = self.prepareSubmitTemplate() @@ -631,23 +631,25 @@ class P4Submit(Command): del(os.environ["P4DIFF"]) diff = read_pipe("p4 diff -du ...") + newdiff = "" for newFile in filesToAdd: - diff += "==== new file ====\n" - diff += "--- /dev/null\n" - diff += "+++ %s\n" % newFile + newdiff += "==== new file ====\n" + newdiff += "--- /dev/null\n" + newdiff += "+++ %s\n" % newFile f = open(newFile, "r") for line in f.readlines(): - diff += "+" + line + newdiff += "+" + line f.close() - separatorLine = "######## everything below this line is just the diff #######" - if platform.system() == "Windows": - separatorLine += "\r" - separatorLine += "\n" + separatorLine = "######## everything below this line is just the diff #######\n" [handle, fileName] = tempfile.mkstemp() tmpFile = os.fdopen(handle, "w+") - tmpFile.write(submitTemplate + separatorLine + diff) + if self.isWindows: + submitTemplate = submitTemplate.replace("\n", "\r\n") + separatorLine = separatorLine.replace("\n", "\r\n") + newdiff = newdiff.replace("\n", "\r\n") + tmpFile.write(submitTemplate + separatorLine + diff + newdiff) tmpFile.close() defaultEditor = "vi" if platform.system() == "Windows": -- cgit v1.2.3 From 6aeeffd14447348f3ab4a7d34927d6aff8c0a75c Mon Sep 17 00:00:00 2001 From: Josh Elsasser Date: Thu, 27 Mar 2008 14:02:14 -0700 Subject: Allow git-cvsserver database table name prefix to be specified. Adds a gitcvs.dbtablenameprefix config variable, the contents of which are prepended to any database tables names used by git-cvsserver. The same substutions as gitcvs.dbname and gitcvs.dbuser are supported, and any non-alphabetic characters are replaced with underscores. A typo found in contrib/completion/git-completion.bash is also fixed. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 5046f69934..791e30f6d7 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -999,7 +999,8 @@ _git_config () gitcvs.enabled gitcvs.logfile gitcvs.allbinary - gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dvpass + gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dbpass + gitcvs.dbtablenameprefix gc.packrefs gc.reflogexpire gc.reflogexpireunreachable -- cgit v1.2.3 From d9e3b7025fdfeb80bb72a10b5d272acfbb5df72a Mon Sep 17 00:00:00 2001 From: Pascal Obry Date: Sun, 6 Apr 2008 19:32:24 +0200 Subject: Add interactive option in rebase command completion list. Signed-off-by: Pascal Obry Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 791e30f6d7..96a712b5ea 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -865,7 +865,7 @@ _git_rebase () return ;; --*) - __gitcomp "--onto --merge --strategy" + __gitcomp "--onto --merge --strategy --interactive" return esac __gitcomp "$(__git_refs)" -- cgit v1.2.3 From aba201c6e8d9934157b9ba39b8e6b54c2fa7b6e1 Mon Sep 17 00:00:00 2001 From: Pascal Obry Date: Sun, 6 Apr 2008 18:56:08 +0200 Subject: Add prefix oriented completions for diff and format-patch commands. Signed-off-by: Pascal Obry Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 96a712b5ea..4d81963b1d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -639,7 +639,9 @@ _git_diff () --find-copies-harder --pickaxe-all --pickaxe-regex --text --ignore-space-at-eol --ignore-space-change --ignore-all-space --exit-code --quiet --ext-diff - --no-ext-diff" + --no-ext-diff + --no-prefix --src-prefix= --dst-prefix= + " return ;; esac @@ -696,6 +698,7 @@ _git_format_patch () --full-index --binary --not --all --cover-letter + --no-prefix --src-prefix= --dst-prefix= " return ;; -- cgit v1.2.3 From f949844263e866774063511c4538b96068dd8d1b Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Wed, 2 Apr 2008 21:35:11 +0200 Subject: contrib/hooks: add an example pre-auto-gc hook It disables git-gc --auto when you are running Linux and you are not on AC. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hooks/pre-auto-gc-battery | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 contrib/hooks/pre-auto-gc-battery (limited to 'contrib') diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery new file mode 100644 index 0000000000..0096f57b7e --- /dev/null +++ b/contrib/hooks/pre-auto-gc-battery @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to verify if you are on battery, in case you +# are running Linux. Called by git-gc --auto with no arguments. The hook +# should exit with non-zero status after issuing an appropriate message +# if it wants to stop the auto repacking. +# +# This hook is stored in the contrib/hooks directory. Your distribution +# may have put this somewhere else. If you want to use this hook, you +# should make this script executable then link to it in the repository +# you would like to use it in. +# +# For example, if the hook is stored in +# /usr/share/git-core/contrib/hooks/pre-auto-gc-battery: +# +# chmod a+x pre-auto-gc-battery +# cd /path/to/your/repository.git +# ln -sf /usr/share/git-core/contrib/hooks/pre-auto-gc-battery \ +# hooks/pre-auto-gc + +if test -x /sbin/on_ac_power && /sbin/on_ac_power +then + exit 0 +elif test "$(cat /sys/class/power_supply/AC/online 2>/dev/null)" = 1 +then + exit 0 +elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null +then + exit 0 +elif grep -q '0x01$' /proc/apm 2>/dev/null +then + exit 0 +fi + +echo "Auto packing deferred; not on AC" +exit 1 -- cgit v1.2.3 From d8abe148bece83f6c3e5ebe6075aef236322ed74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sun, 6 Apr 2008 03:23:43 +0200 Subject: merge, pull: introduce '--(no-)stat' option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This option has the same effect as '--(no-)summary' (i.e. whether to show a diffsat at the end of the merge or not), and it is consistent with the '--stat' option of other git commands. Documentation, tests, and bash completion are updaed accordingly, and the old --summary option is marked as being deprected. Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 4d81963b1d..0f54cfd8a3 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -779,7 +779,7 @@ _git_merge () ;; --*) __gitcomp " - --no-commit --no-summary --squash --strategy + --no-commit --no-stat --squash --strategy " return esac -- cgit v1.2.3 From efb779f8873e5aa36be29a4e551186c62c1b580c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sun, 6 Apr 2008 03:23:46 +0200 Subject: merge, pull: add '--(no-)log' command line option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are the command line option equivalents of the 'merge.log' config variable. The patch also updates documentation and bash completion accordingly, and adds a test. Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0f54cfd8a3..8091d2deb7 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -779,7 +779,7 @@ _git_merge () ;; --*) __gitcomp " - --no-commit --no-stat --squash --strategy + --no-commit --no-stat --log --no-log --squash --strategy " return esac -- cgit v1.2.3 From f45741390844778f5b286cf6607cd767636e5c57 Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Sun, 20 Apr 2008 22:32:47 +0300 Subject: bash: Add completion for git diff --base --ours --theirs Signed-off-by: Teemu Likonen Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 4d81963b1d..6949cac45d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -641,6 +641,7 @@ _git_diff () --ignore-all-space --exit-code --quiet --ext-diff --no-ext-diff --no-prefix --src-prefix= --dst-prefix= + --base --ours --theirs " return ;; -- cgit v1.2.3 From dbe48256b41c1e94d81f2458d7e84b1fdcb47026 Mon Sep 17 00:00:00 2001 From: Clifford Caoile Date: Fri, 18 Apr 2008 22:07:12 +0900 Subject: git.el: Set process-environment instead of invoking env MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the similar patch from David Kågedal [1], "this will make it a little less posix-dependent and more efficient." However, there are two other areas that need to replaced, namely git-run-command-region and git-run-hooks. This patch implements the changes of [1] onto those Emacs Lisp functions. If unpatched, using the git port "msysgit" on Windows will require defadvice changes as shown at [2] (also explained at 4msysgit.git [3]). I have tested git-run-command-region on msysgit, because this is always called by git-commit (via git-commit-tree <- git-do-commit <- git-commit-file). However, I could not test git-run-hooks because it currently does not work on the Emacs Windows port. The latter reports the hooks files as a+rw and a-x, despite msysgit and cygwin chmod setting on the respective files. References: [1] f27e55864317611385be4d33b3c53ca787379df9 [2] http://groups.google.com/group/msysgit/browse_thread/thread/b852fef689817707 [3] http://repo.or.cz/w/git/mingw/4msysgit.git?a=commit;h=3c30e5e87358eba7b6d7dcd6301ae8438f0c30ea Signed-off-by: Clifford Caoile Acked-by: David Kågedal Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 4fa853fae7..2557a7667f 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -232,10 +232,8 @@ and returns the process output as a string, or nil if the git failed." (defun git-run-command-region (buffer start end env &rest args) "Run a git command with specified buffer region as input." - (unless (eq 0 (if env - (git-run-process-region - buffer start end "env" - (append (git-get-env-strings env) (list "git") args)) + (unless (eq 0 (let ((process-environment (append (git-get-env-strings env) + process-environment))) (git-run-process-region buffer start end "git" args))) (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))) @@ -250,9 +248,8 @@ and returns the process output as a string, or nil if the git failed." (erase-buffer) (cd dir) (setq status - (if env - (apply #'call-process "env" nil (list buffer t) nil - (append (git-get-env-strings env) (list hook-name) args)) + (let ((process-environment (append (git-get-env-strings env) + process-environment))) (apply #'call-process hook-name nil (list buffer t) nil args)))) (display-message-or-buffer buffer) (eq 0 status))))) -- cgit v1.2.3 From 3903c6189d0d596a0fef47edab437aa047e812fa Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 20 Apr 2008 14:34:07 -0500 Subject: completion: allow 'git remote' subcommand completion After typing 'git remote ', the subcommand options were not shown. Fix it by adding the missing __gitcomp call. Signed-off-by: Dan McGee Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 4d81963b1d..fd654bdc9c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1052,6 +1052,7 @@ _git_remote () local subcommands="add rm show prune update" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then + __gitcomp "$subcommands" return fi -- cgit v1.2.3 From 799596a5d06f2abddef75940604d00c4bd8ba849 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Sun, 20 Apr 2008 12:28:44 -0500 Subject: completion: remove use of dashed git commands Signed-off-by: Dan McGee Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index fd654bdc9c..6012047ee5 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -152,7 +152,7 @@ __git_heads () done return fi - for i in $(git-ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "$1" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -173,7 +173,7 @@ __git_tags () done return fi - for i in $(git-ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "$1" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -200,7 +200,7 @@ __git_refs () done return fi - for i in $(git-ls-remote "$dir" 2>/dev/null); do + for i in $(git ls-remote "$dir" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -223,7 +223,7 @@ __git_refs2 () __git_refs_remotes () { local cmd i is_hash=y - for i in $(git-ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "$1" 2>/dev/null); do case "$is_hash,$i" in n,refs/heads/*) is_hash=y -- cgit v1.2.3 From 71bd81ade2973a304889d94426c922cc096019a2 Mon Sep 17 00:00:00 2001 From: Andy Parkins Date: Mon, 21 Apr 2008 14:44:44 +0100 Subject: post-receive-email: fix accidental removal of a trailing space in signature line post-receive-email adds a signature to the end of emails in generate_email_footer(). The signature was separated from the main email body using the standard string "-- ". (see RFC 3676) a6080a0 (War on whitespace, 2007-06-07) removed the trailing whitespace from "-- ", leaving it as "--", which is not a correct signature separator. This patch restores the missing space, but does it in a way that will not set off the trailing whitespace alarms. Signed-off-by: Andy Parkins Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 62a740c482..41368950d6 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -202,11 +202,12 @@ generate_email_header() generate_email_footer() { + SPACE=" " cat <<-EOF hooks/post-receive - -- + --${SPACE} $projectdesc EOF } -- cgit v1.2.3 From 07ba53f724b95a817f957b8e943c9e4f545a0949 Mon Sep 17 00:00:00 2001 From: Richard Quirk Date: Sun, 27 Apr 2008 17:35:10 +0200 Subject: bash: Add completion for gitk --merge Option is only completed when .git/MERGE_HEAD is present. Signed-off-by: Richard Quirk Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 665a895f5e..23db664f48 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1346,9 +1346,14 @@ _git () _gitk () { local cur="${COMP_WORDS[COMP_CWORD]}" + local g="$(git rev-parse --git-dir 2>/dev/null)" + local merge="" + if [ -f $g/MERGE_HEAD ]; then + merge="--merge" + fi case "$cur" in --*) - __gitcomp "--not --all" + __gitcomp "--not --all $merge" return ;; esac -- cgit v1.2.3 From 8434c2f1afedb936e0ea8c07ce25733013c2f743 Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Sun, 27 Apr 2008 13:39:30 -0400 Subject: Build in clone Thanks to Johannes Schindelin for various comments and improvements, including supporting cloning full bundles. Signed-off-by: Junio C Hamano --- contrib/examples/git-clone.sh | 523 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 523 insertions(+) create mode 100755 contrib/examples/git-clone.sh (limited to 'contrib') diff --git a/contrib/examples/git-clone.sh b/contrib/examples/git-clone.sh new file mode 100755 index 0000000000..8c7fc7f631 --- /dev/null +++ b/contrib/examples/git-clone.sh @@ -0,0 +1,523 @@ +#!/bin/sh +# +# Copyright (c) 2005, Linus Torvalds +# Copyright (c) 2005, Junio C Hamano +# +# Clone a repository into a different directory that does not yet exist. + +# See git-sh-setup why. +unset CDPATH + +OPTIONS_SPEC="\ +git-clone [options] [--] [] +-- +n,no-checkout don't create a checkout +bare create a bare repository +naked create a bare repository +l,local to clone from a local repository +no-hardlinks don't use local hardlinks, always copy +s,shared setup as a shared repository +template= path to the template directory +q,quiet be quiet +reference= reference repository +o,origin= use instead of 'origin' to track upstream +u,upload-pack= path to git-upload-pack on the remote +depth= create a shallow clone of that depth + +use-separate-remote compatibility, do not use +no-separate-remote compatibility, do not use" + +die() { + echo >&2 "$@" + exit 1 +} + +usage() { + exec "$0" -h +} + +eval "$(echo "$OPTIONS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)" + +get_repo_base() { + ( + cd "`/bin/pwd`" && + cd "$1" || cd "$1.git" && + { + cd .git + pwd + } + ) 2>/dev/null +} + +if [ -n "$GIT_SSL_NO_VERIFY" -o \ + "`git config --bool http.sslVerify`" = false ]; then + curl_extra_args="-k" +fi + +http_fetch () { + # $1 = Remote, $2 = Local + curl -nsfL $curl_extra_args "$1" >"$2" + curl_exit_status=$? + case $curl_exit_status in + 126|127) exit ;; + *) return $curl_exit_status ;; + esac +} + +clone_dumb_http () { + # $1 - remote, $2 - local + cd "$2" && + clone_tmp="$GIT_DIR/clone-tmp" && + mkdir -p "$clone_tmp" || exit 1 + if [ -n "$GIT_CURL_FTP_NO_EPSV" -o \ + "`git config --bool http.noEPSV`" = true ]; then + curl_extra_args="${curl_extra_args} --disable-epsv" + fi + http_fetch "$1/info/refs" "$clone_tmp/refs" || + die "Cannot get remote repository information. +Perhaps git-update-server-info needs to be run there?" + test "z$quiet" = z && v=-v || v= + while read sha1 refname + do + name=`expr "z$refname" : 'zrefs/\(.*\)'` && + case "$name" in + *^*) continue;; + esac + case "$bare,$name" in + yes,* | ,heads/* | ,tags/*) ;; + *) continue ;; + esac + if test -n "$use_separate_remote" && + branch_name=`expr "z$name" : 'zheads/\(.*\)'` + then + tname="remotes/$origin/$branch_name" + else + tname=$name + fi + 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" || + rm -f "$GIT_DIR/REMOTE_HEAD" + if test -f "$GIT_DIR/REMOTE_HEAD"; then + head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"` + case "$head_sha1" in + 'ref: refs/'*) + ;; + *) + git-http-fetch $v -a "$head_sha1" "$1" || + rm -f "$GIT_DIR/REMOTE_HEAD" + ;; + esac + fi +} + +quiet= +local=no +use_local_hardlink=yes +local_shared=no +unset template +no_checkout= +upload_pack= +bare= +reference= +origin= +origin_override= +use_separate_remote=t +depth= +no_progress= +local_explicitly_asked_for= +test -t 1 || no_progress=--no-progress + +while test $# != 0 +do + case "$1" in + -n|--no-checkout) + no_checkout=yes ;; + --naked|--bare) + bare=yes ;; + -l|--local) + local_explicitly_asked_for=yes + use_local_hardlink=yes + ;; + --no-hardlinks) + use_local_hardlink=no ;; + -s|--shared) + local_shared=yes ;; + --template) + shift; template="--template=$1" ;; + -q|--quiet) + quiet=-q ;; + --use-separate-remote|--no-separate-remote) + die "clones are always made with separate-remote layout" ;; + --reference) + shift; reference="$1" ;; + -o|--origin) + shift; + case "$1" in + '') + usage ;; + */*) + die "'$1' is not suitable for an origin name" + esac + git check-ref-format "heads/$1" || + die "'$1' is not suitable for a branch name" + test -z "$origin_override" || + die "Do not give more than one --origin options." + origin_override=yes + origin="$1" + ;; + -u|--upload-pack) + shift + upload_pack="--upload-pack=$1" ;; + --depth) + shift + depth="--depth=$1" ;; + --) + shift + break ;; + *) + usage ;; + esac + shift +done + +repo="$1" +test -n "$repo" || + die 'you must specify a repository to clone.' + +# --bare implies --no-checkout and --no-separate-remote +if test yes = "$bare" +then + if test yes = "$origin_override" + then + die '--bare and --origin $origin options are incompatible.' + fi + no_checkout=yes + use_separate_remote= +fi + +if test -z "$origin" +then + origin=origin +fi + +# Turn the source into an absolute path if +# it is local +if base=$(get_repo_base "$repo"); then + repo="$base" + if test -z "$depth" + then + local=yes + fi +elif test -f "$repo" +then + case "$repo" in /*) ;; *) repo="$PWD/$repo" ;; esac +fi + +# Decide the directory name of the new repository +if test -n "$2" +then + dir="$2" + test $# = 2 || die "excess parameter to git-clone" +else + # Derive one from the repository name + # Try using "humanish" part of source repo if user didn't specify one + if test -f "$repo" + then + # Cloning from a bundle + dir=$(echo "$repo" | sed -e 's|/*\.bundle$||' -e 's|.*/||g') + else + dir=$(echo "$repo" | + sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g') + fi +fi + +[ -e "$dir" ] && die "destination directory '$dir' already exists." +[ yes = "$bare" ] && unset GIT_WORK_TREE +[ -n "$GIT_WORK_TREE" ] && [ -e "$GIT_WORK_TREE" ] && +die "working tree '$GIT_WORK_TREE' already exists." +D= +W= +cleanup() { + err=$? + test -z "$D" && rm -rf "$dir" + test -z "$W" && test -n "$GIT_WORK_TREE" && rm -rf "$GIT_WORK_TREE" + cd .. + test -n "$D" && rm -rf "$D" + test -n "$W" && rm -rf "$W" + exit $err +} +trap cleanup 0 +mkdir -p "$dir" && D=$(cd "$dir" && pwd) || usage +test -n "$GIT_WORK_TREE" && mkdir -p "$GIT_WORK_TREE" && +W=$(cd "$GIT_WORK_TREE" && pwd) && GIT_WORK_TREE="$W" && export GIT_WORK_TREE +if test yes = "$bare" || test -n "$GIT_WORK_TREE"; then + GIT_DIR="$D" +else + GIT_DIR="$D/.git" +fi && +export GIT_DIR && +GIT_CONFIG="$GIT_DIR/config" git-init $quiet ${template+"$template"} || usage + +if test -n "$bare" +then + GIT_CONFIG="$GIT_DIR/config" git config core.bare true +fi + +if test -n "$reference" +then + ref_git= + if test -d "$reference" + then + if test -d "$reference/.git/objects" + then + ref_git="$reference/.git" + elif test -d "$reference/objects" + then + ref_git="$reference" + fi + fi + if test -n "$ref_git" + then + ref_git=$(cd "$ref_git" && pwd) + echo "$ref_git/objects" >"$GIT_DIR/objects/info/alternates" + ( + GIT_DIR="$ref_git" git for-each-ref \ + --format='%(objectname) %(*objectname)' + ) | + while read a b + do + test -z "$a" || + git update-ref "refs/reference-tmp/$a" "$a" + test -z "$b" || + git update-ref "refs/reference-tmp/$b" "$b" + done + else + die "reference repository '$reference' is not a local directory." + fi +fi + +rm -f "$GIT_DIR/CLONE_HEAD" + +# We do local magic only when the user tells us to. +case "$local" in +yes) + ( cd "$repo/objects" ) || + die "cannot chdir to local '$repo/objects'." + + if test "$local_shared" = yes + then + mkdir -p "$GIT_DIR/objects/info" + echo "$repo/objects" >>"$GIT_DIR/objects/info/alternates" + else + cpio_quiet_flag="" + cpio --help 2>&1 | grep -- --quiet >/dev/null && \ + cpio_quiet_flag=--quiet + l= && + if test "$use_local_hardlink" = yes + then + # See if we can hardlink and drop "l" if not. + sample_file=$(cd "$repo" && \ + find objects -type f -print | sed -e 1q) + # objects directory should not be empty because + # we are cloning! + test -f "$repo/$sample_file" || + die "fatal: cannot clone empty repository" + if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null + then + rm -f "$GIT_DIR/objects/sample" + l=l + elif test -n "$local_explicitly_asked_for" + then + echo >&2 "Warning: -l asked but cannot hardlink to $repo" + fi + fi && + cd "$repo" && + find objects -depth -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/" || \ + exit 1 + fi + git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1 + ;; +*) + case "$repo" in + rsync://*) + case "$depth" in + "") ;; + *) die "shallow over rsync not supported" ;; + esac + rsync $quiet -av --ignore-existing \ + --exclude info "$repo/objects/" "$GIT_DIR/objects/" || + exit + # Look at objects/info/alternates for rsync -- http will + # support it natively and git native ones will do it on the + # remote end. Not having that file is not a crime. + rsync -q "$repo/objects/info/alternates" \ + "$GIT_DIR/TMP_ALT" 2>/dev/null || + rm -f "$GIT_DIR/TMP_ALT" + if test -f "$GIT_DIR/TMP_ALT" + then + ( cd "$D" && + . git-parse-remote && + resolve_alternates "$repo" <"$GIT_DIR/TMP_ALT" ) | + while read alt + do + case "$alt" in 'bad alternate: '*) die "$alt";; esac + case "$quiet" in + '') echo >&2 "Getting alternate: $alt" ;; + esac + rsync $quiet -av --ignore-existing \ + --exclude info "$alt" "$GIT_DIR/objects" || exit + done + rm -f "$GIT_DIR/TMP_ALT" + fi + git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1 + ;; + https://*|http://*|ftp://*) + case "$depth" in + "") ;; + *) die "shallow over http or ftp not supported" ;; + esac + if test -z "@@NO_CURL@@" + then + clone_dumb_http "$repo" "$D" + else + die "http transport not supported, rebuild Git with curl support" + fi + ;; + *) + if [ -f "$repo" ] ; then + git bundle unbundle "$repo" > "$GIT_DIR/CLONE_HEAD" || + die "unbundle from '$repo' failed." + else + case "$upload_pack" in + '') git-fetch-pack --all -k $quiet $depth $no_progress "$repo";; + *) git-fetch-pack --all -k \ + $quiet "$upload_pack" $depth $no_progress "$repo" ;; + esac >"$GIT_DIR/CLONE_HEAD" || + die "fetch-pack from '$repo' failed." + fi + ;; + esac + ;; +esac +test -d "$GIT_DIR/refs/reference-tmp" && rm -fr "$GIT_DIR/refs/reference-tmp" + +if test -f "$GIT_DIR/CLONE_HEAD" +then + # Read git-fetch-pack -k output and store the remote branches. + if [ -n "$use_separate_remote" ] + then + branch_top="remotes/$origin" + else + branch_top="heads" + fi + tag_top="tags" + while read sha1 name + do + case "$name" in + *'^{}') + continue ;; + HEAD) + destname="REMOTE_HEAD" ;; + refs/heads/*) + destname="refs/$branch_top/${name#refs/heads/}" ;; + refs/tags/*) + destname="refs/$tag_top/${name#refs/tags/}" ;; + *) + continue ;; + esac + git update-ref -m "clone: from $repo" "$destname" "$sha1" "" + done < "$GIT_DIR/CLONE_HEAD" +fi + +if test -n "$W"; then + cd "$W" || exit +else + cd "$D" || exit +fi + +if test -z "$bare" +then + # a non-bare repository is always in separate-remote layout + remote_top="refs/remotes/$origin" + head_sha1= + test ! -r "$GIT_DIR/REMOTE_HEAD" || head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"` + case "$head_sha1" in + 'ref: refs/'*) + # Uh-oh, the remote told us (http transport done against + # new style repository with a symref HEAD). + # Ideally we should skip the guesswork but for now + # opt for minimum change. + head_sha1=`expr "z$head_sha1" : 'zref: refs/heads/\(.*\)'` + head_sha1=`cat "$GIT_DIR/$remote_top/$head_sha1"` + ;; + esac + + # The name under $remote_top the remote HEAD seems to point at. + head_points_at=$( + ( + test -f "$GIT_DIR/$remote_top/master" && echo "master" + cd "$GIT_DIR/$remote_top" && + find . -type f -print | sed -e 's/^\.\///' + ) | ( + done=f + while read name + do + test t = $done && continue + branch_tip=`cat "$GIT_DIR/$remote_top/$name"` + if test "$head_sha1" = "$branch_tip" + then + echo "$name" + done=t + fi + done + ) + ) + + # Upstream URL + git config remote."$origin".url "$repo" && + + # Set up the mappings to track the remote branches. + git config remote."$origin".fetch \ + "+refs/heads/*:$remote_top/*" '^$' && + + # Write out remote.$origin config, and update our "$head_points_at". + case "$head_points_at" in + ?*) + # Local default branch + git symbolic-ref HEAD "refs/heads/$head_points_at" && + + # Tracking branch for the primary branch at the remote. + git update-ref HEAD "$head_sha1" && + + rm -f "refs/remotes/$origin/HEAD" + git symbolic-ref "refs/remotes/$origin/HEAD" \ + "refs/remotes/$origin/$head_points_at" && + + git config branch."$head_points_at".remote "$origin" && + git config branch."$head_points_at".merge "refs/heads/$head_points_at" + ;; + '') + if test -z "$head_sha1" + then + # Source had nonexistent ref in HEAD + echo >&2 "Warning: Remote HEAD refers to nonexistent ref, unable to checkout." + no_checkout=t + else + # Source had detached HEAD pointing nowhere + git update-ref --no-deref HEAD "$head_sha1" && + rm -f "refs/remotes/$origin/HEAD" + fi + ;; + esac + + case "$no_checkout" in + '') + test "z$quiet" = z -a "z$no_progress" = z && v=-v || v= + git read-tree -m -u $v HEAD HEAD + esac +fi +rm -f "$GIT_DIR/CLONE_HEAD" "$GIT_DIR/REMOTE_HEAD" + +trap - 0 -- cgit v1.2.3 From 97561fff3263add59ec25207a0c5a635b28ce9b9 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 25 May 2008 22:17:57 -0400 Subject: Don't diff empty tree on branch creation in paranoid update hook Listing all files in a branch during branch creation is silly; the user's file-level ACLs probably don't mean anything at this point. We now treat the base case of 0{40} as an empty diff, as this happens only when the user is creating the branch and there are file level ACLs that diff against the old value of the branch. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 068fa37083..6e0d97c89f 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -225,14 +225,12 @@ sub load_diff ($) { local $/ = "\0"; my %this_diff; if ($base =~ /^0{40}$/) { - open(T,'-|','git','ls-tree', - '-r','--name-only','-z', - $new) or return undef; - while () { - chop; - $this_diff{$_} = 'A'; - } - close T or return undef; + # Don't load the diff at all; we are making the + # branch and have no base to compare to in this + # case. A file level ACL makes no sense in this + # context. Having an empty diff will allow the + # branch creation. + # } else { open(T,'-|','git','diff-tree', '-r','--name-status','-z', -- cgit v1.2.3 From 50b7b2ee99cb98265f847d91159cb3215c6f2379 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 25 May 2008 22:18:01 -0400 Subject: Don't load missing ACL files in paranoid update hook If a user or group ACL file does not exist in the current tip revision of the acl repository we will get an error from cat-file when we ask for that blob as it cannot be resolved. A quick look at the history by rev-list can tell us if there is a path there or not. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index 6e0d97c89f..ae94822cd3 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -136,6 +136,7 @@ sub parse_config ($$$$) { local $ENV{GIT_DIR} = shift; my $br = shift; my $fn = shift; + return unless git_value('rev-list','--max-count=1',$br,'--',$fn); info "Loading $br:$fn"; open(I,'-|','git','cat-file','blob',"$br:$fn"); my $section = ''; -- cgit v1.2.3 From fa620f1ac8191fa72e54b8b6acc3e424ecfae26e Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 25 May 2008 22:18:05 -0400 Subject: Ignore no-op changes in paranoid update hook If the hook gets invoked with identical old and new ids there is no change taking place. We probably should not have been called, but instead of failing silently allow the no-op. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/hooks/update-paranoid | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid index ae94822cd3..d18b317b2f 100644 --- a/contrib/hooks/update-paranoid +++ b/contrib/hooks/update-paranoid @@ -259,6 +259,7 @@ 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; +grant "No change requested." if $old eq $new; $repository_name = File::Spec->rel2abs($git_dir); $repository_name =~ m,/([^/]+)(?:\.git|/\.git)$,; -- cgit v1.2.3 From 37a12dda24f7ab250869db7eb00157f78d40c724 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 26 May 2008 14:20:54 +0100 Subject: hg-to-git: add --verbose option This patch adds an option to make hg-to-git quiet by default. Note: it only suppresses those messages that would be printed when everything was up-to-date. Signed-off-by: Johannes Schindelin Acked-by: Stelian Pop Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index d72ffbb777..f68ef725d4 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -46,6 +46,7 @@ options: for incrementals -n, --nrepack=INT: number of changesets that will trigger a repack (default=0, -1 to deactivate) + -v, --verbose: be verbose required: hgprj: name of the HG project to import (directory) @@ -75,15 +76,18 @@ def getgitenv(user, date): state = '' opt_nrepack = 0 +verbose = False try: - opts, args = getopt.getopt(sys.argv[1:], 's:t:n:', ['gitstate=', 'tempdir=', 'nrepack=']) + opts, args = getopt.getopt(sys.argv[1:], 's:t:n:v', ['gitstate=', 'tempdir=', 'nrepack=', 'verbose']) for o, a in opts: if o in ('-s', '--gitstate'): state = a state = os.path.abspath(state) if o in ('-n', '--nrepack'): opt_nrepack = int(a) + if o in ('-v', '--verbose'): + verbose = True if len(args) != 1: raise('params') except: @@ -95,17 +99,20 @@ os.chdir(hgprj) if state: if os.path.exists(state): - print 'State does exist, reading' + if verbose: + print 'State does exist, reading' f = open(state, 'r') hgvers = pickle.load(f) else: print 'State does not exist, first run' tip = os.popen('hg tip --template "{rev}"').read() -print 'tip is', tip +if verbose: + print 'tip is', tip # Calculate the branches -print 'analysing the branches...' +if verbose: + print 'analysing the branches...' hgchildren["0"] = () hgparents["0"] = (None, None) hgbranch["0"] = "master" @@ -232,7 +239,8 @@ if hgnewcsets >= opt_nrepack and opt_nrepack != -1: # write the state for incrementals if state: - print 'Writing state' + if verbose: + print 'Writing state' f = open(state, 'w') pickle.dump(hgvers, f) -- cgit v1.2.3 From 3db4723ead0f141540118f622dedac5106b07a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karl=20Hasselstr=C3=B6m?= Date: Tue, 3 Jun 2008 00:41:44 +0200 Subject: Revert "git.el: Set process-environment instead of invoking env" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit dbe48256b41c1e94d81f2458d7e84b1fdcb47026, which caused mis-encoding of non-ASCII author/committer names when the git-status mode is used to create commits. Signed-off-by: Karl Hasselström Signed-off-by: Junio C Hamano --- contrib/emacs/git.el | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 2557a7667f..4fa853fae7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -232,8 +232,10 @@ and returns the process output as a string, or nil if the git failed." (defun git-run-command-region (buffer start end env &rest args) "Run a git command with specified buffer region as input." - (unless (eq 0 (let ((process-environment (append (git-get-env-strings env) - process-environment))) + (unless (eq 0 (if env + (git-run-process-region + buffer start end "env" + (append (git-get-env-strings env) (list "git") args)) (git-run-process-region buffer start end "git" args))) (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))) @@ -248,8 +250,9 @@ and returns the process output as a string, or nil if the git failed." (erase-buffer) (cd dir) (setq status - (let ((process-environment (append (git-get-env-strings env) - process-environment))) + (if env + (apply #'call-process "env" nil (list buffer t) nil + (append (git-get-env-strings env) (list hook-name) args)) (apply #'call-process hook-name nil (list buffer t) nil args)))) (display-message-or-buffer buffer) (eq 0 status))))) -- cgit v1.2.3 From 1d284cbae3abd5fb6f58dd5282ba0e93eb68e6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sat, 14 Jun 2008 11:48:01 +0200 Subject: completion: add more 'git add' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 16984632d9..2141b6b6ba 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -500,7 +500,10 @@ _git_add () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--interactive --refresh" + __gitcomp " + --interactive --refresh --patch --update --dry-run + --ignore-errors + " return esac COMPREPLY=() -- cgit v1.2.3 From 20827d99c5ee079d92831474a0b6e66b79757dbd Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 19 Jun 2008 16:15:53 -0500 Subject: completion: add --graph to log command completion Signed-off-by: Dan McGee Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2141b6b6ba..0eb8df020b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -761,6 +761,7 @@ _git_log () --pretty= --name-status --name-only --raw --not --all --left-right --cherry-pick + --graph " return ;; -- cgit v1.2.3 From 0c3d26d24ab913a8cc6f478d176f5896af28c03c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Sandstr=C3=B6m?= Date: Fri, 20 Jun 2008 01:21:33 +0200 Subject: Add a helper script to send patches with Mozilla Thunderbird MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The script appp.sh can be used with the External Editor extension for Mozilla Thunderbird in order to be able to send inline patches in an easy way. Signed-off-by: Lukas Sandström Signed-off-by: Junio C Hamano --- contrib/thunderbird-patch-inline/README | 20 ++++++++++++ contrib/thunderbird-patch-inline/appp.sh | 55 ++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 contrib/thunderbird-patch-inline/README create mode 100755 contrib/thunderbird-patch-inline/appp.sh (limited to 'contrib') diff --git a/contrib/thunderbird-patch-inline/README b/contrib/thunderbird-patch-inline/README new file mode 100644 index 0000000000..39f96aa115 --- /dev/null +++ b/contrib/thunderbird-patch-inline/README @@ -0,0 +1,20 @@ +appp.sh is a script that is supposed to be used together with ExternalEditor +for Mozilla Thundebird. It will let you include patches inline in e-mails +in an easy way. + +Usage: +- Generate the patch with git format-patch. +- Start writing a new e-mail in Thunderbird. +- Press the external editor button (or Ctrl-E) to run appp.sh +- Select the previosly generated patch file. +- Finish editing the e-mail. + +Any text that is entered into the message editor before appp.sh is called +will be moved to the section between the --- and the diffstat. + +All S-O-B:s and Cc:s in the patch will be added to the CC list. + +To set it up, just install External Editor and tell it to use appp.sh as the +editor. + +Zenity is a required dependency. diff --git a/contrib/thunderbird-patch-inline/appp.sh b/contrib/thunderbird-patch-inline/appp.sh new file mode 100755 index 0000000000..cc518f3c89 --- /dev/null +++ b/contrib/thunderbird-patch-inline/appp.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# Copyright 2008 Lukas Sandström +# +# AppendPatch - A script to be used together with ExternalEditor +# for Mozilla Thunderbird to properly include pathes inline i e-mails. + +# ExternalEditor can be downloaded at http://globs.org/articles.php?lng=en&pg=2 + +CONFFILE=~/.appprc + +SEP="-=-=-=-=-=-=-=-=-=# Don't remove this line #=-=-=-=-=-=-=-=-=-" +if [ -e "$CONFFILE" ] ; then + LAST_DIR=`grep -m 1 "^LAST_DIR=" "${CONFFILE}"|sed -e 's/^LAST_DIR=//'` + cd "${LAST_DIR}" +else + cd > /dev/null +fi + +PATCH=$(zenity --file-selection) + +if [ "$?" != "0" ] ; then + #zenity --error --text "No patchfile given." + exit 1 +fi + +cd - > /dev/null + +SUBJECT=`sed -n -e '/^Subject: /p' "${PATCH}"` +HEADERS=`sed -e '/^'"${SEP}"'$/,$d' $1` +BODY=`sed -e "1,/${SEP}/d" $1` +CMT_MSG=`sed -e '1,/^$/d' -e '/^---$/,$d' "${PATCH}"` +DIFF=`sed -e '1,/^---$/d' "${PATCH}"` + +CCS=`echo -e "$CMT_MSG\n$HEADERS" | sed -n -e 's/^Cc: \(.*\)$/\1,/gp' \ + -e 's/^Signed-off-by: \(.*\)/\1,/gp'` + +echo "$SUBJECT" > $1 +echo "Cc: $CCS" >> $1 +echo "$HEADERS" | sed -e '/^Subject: /d' -e '/^Cc: /d' >> $1 +echo "$SEP" >> $1 + +echo "$CMT_MSG" >> $1 +echo "---" >> $1 +if [ "x${BODY}x" != "xx" ] ; then + echo >> $1 + echo "$BODY" >> $1 + echo >> $1 +fi +echo "$DIFF" >> $1 + +LAST_DIR=`dirname "${PATCH}"` + +grep -v "^LAST_DIR=" "${CONFFILE}" > "${CONFFILE}_" +echo "LAST_DIR=${LAST_DIR}" >> "${CONFFILE}_" +mv "${CONFFILE}_" "${CONFFILE}" -- cgit v1.2.3 From 66aafad5e43815e5f54634e4ef787cd759388880 Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Fri, 20 Jun 2008 16:02:10 +0300 Subject: bash: Add more option completions for 'git log' Options added: --walk-reflogs --stat --numstat --shortstat --decorate --diff-filter= --color-words Signed-off-by: Teemu Likonen Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0eb8df020b..ebf7cde5c0 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -762,6 +762,9 @@ _git_log () --not --all --left-right --cherry-pick --graph + --stat --numstat --shortstat + --decorate --diff-filter= + --color-words --walk-reflogs " return ;; -- cgit v1.2.3 From 4c2d5d722c4775c1efd5e63f41ba5b303ec8fb65 Mon Sep 17 00:00:00 2001 From: Jing Xue Date: Sun, 22 Jun 2008 14:12:39 -0400 Subject: Add 'git-p4.allowSubmit' to git-p4 I'm working with a perforce repo using git-p4. There are some config files which I need to change locally according to my environment. I'm using a 'local' git branch to park these changes. And I want to avoid accidentally checking them into p4 just by doing "git p4 submit" mindlessly without realizing which branch I'm actually on. This patch adds a new git config, 'git-p4.allowSubmit', which is a whitelist of branch names. "git p4 submit" will only allow submissions from local branches on the list. Useful for preventing inadvertently submitting from a strictly local branch. For backward compatibility, if this config is not set at all, submissions from all branches are allowed. Signed-off-by: Jing Xue Acked-By: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d8de9f6c25..87ca51e401 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -687,6 +687,10 @@ class P4Submit(Command): else: return False + allowSubmit = gitConfig("git-p4.allowSubmit") + if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","): + die("%s is not in git-p4.allowSubmit" % self.master) + [upstream, settings] = findUpstreamBranchPoint() self.depotPath = settings['depot-paths'][0] if len(self.origin) == 0: -- cgit v1.2.3 From 8813df9066427ffe40d1d77d18f20643f396f153 Mon Sep 17 00:00:00 2001 From: Olivier Marin Date: Fri, 27 Jun 2008 02:17:55 +0200 Subject: Documentation: remove {show,whatchanged}.difftree config options This removes, from the documentation and the bash completion script, the two config options that were introduced by the git-whatchanged.sh script and lost in the C rewrite. Today, we can use aliases as an alternative. Signed-off-by: Olivier Marin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 -- 1 file changed, 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index ebf7cde5c0..3f46149853 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1041,7 +1041,6 @@ _git_config () pull.octopus pull.twohead repack.useDeltaBaseOffset - show.difftree showbranch.default tar.umask transfer.unpackLimit @@ -1050,7 +1049,6 @@ _git_config () user.name user.email user.signingkey - whatchanged.difftree branch. remote. " } -- cgit v1.2.3 From be612c2318e87a9d32a385c7684459e9e0cd6227 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 30 Jun 2008 19:50:44 +0100 Subject: Add another fast-import example, this time for .zip files Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/fast-import/import-zips.py | 72 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100755 contrib/fast-import/import-zips.py (limited to 'contrib') diff --git a/contrib/fast-import/import-zips.py b/contrib/fast-import/import-zips.py new file mode 100755 index 0000000000..c674fa2d1b --- /dev/null +++ b/contrib/fast-import/import-zips.py @@ -0,0 +1,72 @@ +#!/usr/bin/python + +## zip archive frontend for git-fast-import +## +## For example: +## +## mkdir project; cd project; git init +## python import-zips.py *.zip +## git log --stat import-zips + +from os import popen, path +from sys import argv, exit +from time import mktime +from zipfile import ZipFile + +if len(argv) < 2: + print 'Usage:', argv[0], '...' + exit(1) + +branch_ref = 'refs/heads/import-zips' +committer_name = 'Z Ip Creator' +committer_email = 'zip@example.com' + +fast_import = popen('git fast-import --quiet', 'w') +def printlines(list): + for str in list: + fast_import.write(str + "\n") + +for zipfile in argv[1:]: + commit_time = 0 + next_mark = 1 + common_prefix = None + mark = dict() + + zip = ZipFile(zipfile, 'r') + for name in zip.namelist(): + if name.endswith('/'): + continue + info = zip.getinfo(name) + + if commit_time < info.date_time: + commit_time = info.date_time + if common_prefix == None: + common_prefix = name[:name.rfind('/') + 1] + else: + while not name.startswith(common_prefix): + common_prefix = name[:name.rfind('/') + 1] + + mark[name] = ':' + str(next_mark) + next_mark += 1 + + printlines(('blob', 'mark ' + mark[name], \ + 'data ' + str(info.file_size))) + fast_import.write(zip.read(name) + "\n") + + committer = committer_name + ' <' + committer_email + '> %d +0000' % \ + mktime(commit_time + (0, 0, 0)) + + printlines(('commit ' + branch_ref, 'committer ' + committer, \ + 'data < Date: Fri, 27 Jun 2008 16:37:15 +0200 Subject: stash: introduce 'stash save --keep-index' option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'git stash save' saves local modifications to a new stash, and runs 'git reset --hard' to revert them to a clean index and work tree. When the '--keep-index' option is specified, after that 'git reset --hard' the previous contents of the index is restored and the work tree is updated to match the index. This option is useful if the user wants to commit only parts of his local modifications, but wants to test those parts before committing. Also add support for the completion of the new option, and add an example use case to the documentation. Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3f46149853..595de80ea4 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1137,8 +1137,19 @@ _git_show () _git_stash () { local subcommands='save list show apply clear drop pop create' - if [ -z "$(__git_find_subcommand "$subcommands")" ]; then + local subcommand="$(__git_find_subcommand "$subcommands")" + if [ -z "$subcommand" ]; then __gitcomp "$subcommands" + else + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$subcommand,$cur" in + save,--*) + __gitcomp "--keep-index" + ;; + *) + COMPREPLY=() + ;; + esac fi } -- cgit v1.2.3 From 6376cffaebe40947eea9afb4ae6df05a6ac59ae8 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sun, 6 Jul 2008 05:15:17 +0200 Subject: hg-to-git: avoid raising a string exception This fixes the following warning: hg-to-git.py:92: DeprecationWarning: raising a string exception is deprecated Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index f68ef725d4..25d99411ca 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -89,7 +89,7 @@ try: if o in ('-v', '--verbose'): verbose = True if len(args) != 1: - raise('params') + raise Exception('params') except: usage() sys.exit(1) -- cgit v1.2.3 From 2553ede5a9b09260be69de72b60e5038f5452c44 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sun, 6 Jul 2008 05:15:18 +0200 Subject: hg-to-git: abort if the project directory is not a hg repo Check the exit code of the first hg command, and abort to avoid a later ValueError exception. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 25d99411ca..130b1c4bcd 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -106,7 +106,10 @@ if state: else: print 'State does not exist, first run' -tip = os.popen('hg tip --template "{rev}"').read() +sock = os.popen('hg tip --template "{rev}"') +tip = sock.read() +if sock.close(): + sys.exit(1) if verbose: print 'tip is', tip -- cgit v1.2.3 From 96f2395951fd81ad49217d49074dfbcf3f0df685 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sun, 6 Jul 2008 05:15:19 +0200 Subject: hg-to-git: rewrite "git-frotz" to "git frotz" This is not just nice but necessary since git-frotz is no longer in PATH. Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 130b1c4bcd..61540ef806 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -152,7 +152,7 @@ for cset in range(1, int(tip) + 1): if not hgvers.has_key("0"): print 'creating repository' - os.system('git-init-db') + os.system('git init-db') # loop through every hg changeset for cset in range(int(tip) + 1): @@ -194,10 +194,10 @@ for cset in range(int(tip) + 1): if cset != 0: if hgbranch[str(cset)] == "branch-" + str(cset): print 'creating new branch', hgbranch[str(cset)] - os.system('git-checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent])) + os.system('git checkout -b %s %s' % (hgbranch[str(cset)], hgvers[parent])) else: print 'checking out branch', hgbranch[str(cset)] - os.system('git-checkout %s' % hgbranch[str(cset)]) + os.system('git checkout %s' % hgbranch[str(cset)]) # merge if mparent: @@ -206,7 +206,7 @@ for cset in range(int(tip) + 1): else: otherbranch = hgbranch[parent] print 'merging', otherbranch, 'into', hgbranch[str(cset)] - os.system(getgitenv(user, date) + 'git-merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch)) + os.system(getgitenv(user, date) + 'git merge --no-commit -s ours "" %s %s' % (hgbranch[str(cset)], otherbranch)) # remove everything except .git and .hg directories os.system('find . \( -path "./.hg" -o -path "./.git" \) -prune -o ! -name "." -print | xargs rm -rf') @@ -215,9 +215,9 @@ for cset in range(int(tip) + 1): os.system('hg update -C %d' % cset) # add new files - os.system('git-ls-files -x .hg --others | git-update-index --add --stdin') + os.system('git ls-files -x .hg --others | git update-index --add --stdin') # delete removed files - os.system('git-ls-files -x .hg --deleted | git-update-index --remove --stdin') + os.system('git ls-files -x .hg --deleted | git update-index --remove --stdin') # commit os.system(getgitenv(user, date) + 'git commit --allow-empty -a -F %s' % filecomment) @@ -225,20 +225,20 @@ for cset in range(int(tip) + 1): # tag if tag and tag != 'tip': - os.system(getgitenv(user, date) + 'git-tag %s' % tag) + os.system(getgitenv(user, date) + 'git tag %s' % tag) # delete branch if not used anymore... if mparent and len(hgchildren[str(cset)]): print "Deleting unused branch:", otherbranch - os.system('git-branch -d %s' % otherbranch) + os.system('git branch -d %s' % otherbranch) # retrieve and record the version - vvv = os.popen('git-show --quiet --pretty=format:%H').read() + vvv = os.popen('git show --quiet --pretty=format:%H').read() print 'record', cset, '->', vvv hgvers[str(cset)] = vvv if hgnewcsets >= opt_nrepack and opt_nrepack != -1: - os.system('git-repack -a -d') + os.system('git repack -a -d') # write the state for incrementals if state: -- cgit v1.2.3 From af9a01e1c2f6a8814b817eb7f3d78814389a3212 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Sun, 6 Jul 2008 05:15:20 +0200 Subject: hg-to-git: use git init instead of git init-db Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hg-to-git/hg-to-git.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py index 61540ef806..7b03204ed1 100755 --- a/contrib/hg-to-git/hg-to-git.py +++ b/contrib/hg-to-git/hg-to-git.py @@ -152,7 +152,7 @@ for cset in range(1, int(tip) + 1): if not hgvers.has_key("0"): print 'creating repository' - os.system('git init-db') + os.system('git init') # loop through every hg changeset for cset in range(int(tip) + 1): -- cgit v1.2.3 From bf11d4613c647184bb081fa600f5c0ab8c0be909 Mon Sep 17 00:00:00 2001 From: Dmitry Potapov Date: Wed, 2 Jul 2008 17:29:50 +0400 Subject: completion.bash: add 'skip' and 'run' to git-bisect Signed-off-by: Dmitry Potapov Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3f46149853..d54aa8d62c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -511,7 +511,7 @@ _git_add () _git_bisect () { - local subcommands="start bad good reset visualize replay log" + local subcommands="start bad good skip reset visualize replay log run" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" @@ -519,7 +519,7 @@ _git_bisect () fi case "$subcommand" in - bad|good|reset) + bad|good|reset|skip) __gitcomp "$(__git_refs)" ;; *) -- cgit v1.2.3 From 1c7b76be7d620bbaf2e6b8417f04012326bbb9df Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Mon, 7 Jul 2008 19:24:20 +0200 Subject: Build in merge Mentored-by: Johannes Schindelin Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/examples/git-merge.sh | 554 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100755 contrib/examples/git-merge.sh (limited to 'contrib') diff --git a/contrib/examples/git-merge.sh b/contrib/examples/git-merge.sh new file mode 100755 index 0000000000..8026ccff4a --- /dev/null +++ b/contrib/examples/git-merge.sh @@ -0,0 +1,554 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# + +OPTIONS_KEEPDASHDASH= +OPTIONS_SPEC="\ +git-merge [options] ... +git-merge [options] HEAD +-- +stat show a diffstat at the end of the merge +n don't show a diffstat at the end of the merge +summary (synonym to --stat) +log add list of one-line log to merge commit message +squash create a single commit instead of doing a merge +commit perform a commit if the merge succeeds (default) +ff allow fast forward (default) +s,strategy= merge strategy to use +m,message= message to be used for the merge commit (if any) +" + +SUBDIRECTORY_OK=Yes +. git-sh-setup +require_work_tree +cd_to_toplevel + +test -z "$(git ls-files -u)" || + die "You are in the middle of a conflicted merge." + +LF=' +' + +all_strategies='recur recursive octopus resolve stupid ours subtree' +default_twohead_strategies='recursive' +default_octopus_strategies='octopus' +no_fast_forward_strategies='subtree ours' +no_trivial_strategies='recursive recur subtree ours' +use_strategies= + +allow_fast_forward=t +allow_trivial_merge=t +squash= no_commit= log_arg= + +dropsave() { + rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \ + "$GIT_DIR/MERGE_STASH" || exit 1 +} + +savestate() { + # Stash away any local modifications. + git stash create >"$GIT_DIR/MERGE_STASH" +} + +restorestate() { + if test -f "$GIT_DIR/MERGE_STASH" + then + git reset --hard $head >/dev/null + git stash apply $(cat "$GIT_DIR/MERGE_STASH") + git update-index --refresh >/dev/null + fi +} + +finish_up_to_date () { + case "$squash" in + t) + echo "$1 (nothing to squash)" ;; + '') + echo "$1" ;; + esac + dropsave +} + +squash_message () { + echo Squashed commit of the following: + echo + git log --no-merges --pretty=medium ^"$head" $remoteheads +} + +finish () { + if test '' = "$2" + then + rlogm="$GIT_REFLOG_ACTION" + else + echo "$2" + rlogm="$GIT_REFLOG_ACTION: $2" + fi + case "$squash" in + t) + echo "Squash commit -- not updating HEAD" + squash_message >"$GIT_DIR/SQUASH_MSG" + ;; + '') + case "$merge_msg" in + '') + echo "No merge message -- not updating HEAD" + ;; + *) + git update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1 + git gc --auto + ;; + esac + ;; + esac + case "$1" in + '') + ;; + ?*) + if test "$show_diffstat" = t + then + # We want color (if set), but no pager + GIT_PAGER='' git diff --stat --summary -M "$head" "$1" + fi + ;; + esac + + # Run a post-merge hook + if test -x "$GIT_DIR"/hooks/post-merge + then + case "$squash" in + t) + "$GIT_DIR"/hooks/post-merge 1 + ;; + '') + "$GIT_DIR"/hooks/post-merge 0 + ;; + esac + fi +} + +merge_name () { + remote="$1" + rh=$(git rev-parse --verify "$remote^0" 2>/dev/null) || return + bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null) + if test "$rh" = "$bh" + then + echo "$rh branch '$remote' of ." + elif truname=$(expr "$remote" : '\(.*\)~[1-9][0-9]*$') && + git show-ref -q --verify "refs/heads/$truname" 2>/dev/null + then + echo "$rh branch '$truname' (early part) of ." + elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD" + then + sed -e 's/ not-for-merge / /' -e 1q \ + "$GIT_DIR/FETCH_HEAD" + else + echo "$rh commit '$remote'" + fi +} + +parse_config () { + while test $# != 0; do + case "$1" in + -n|--no-stat|--no-summary) + show_diffstat=false ;; + --stat|--summary) + show_diffstat=t ;; + --log|--no-log) + log_arg=$1 ;; + --squash) + test "$allow_fast_forward" = t || + die "You cannot combine --squash with --no-ff." + squash=t no_commit=t ;; + --no-squash) + squash= no_commit= ;; + --commit) + no_commit= ;; + --no-commit) + no_commit=t ;; + --ff) + allow_fast_forward=t ;; + --no-ff) + test "$squash" != t || + die "You cannot combine --squash with --no-ff." + allow_fast_forward=f ;; + -s|--strategy) + shift + case " $all_strategies " in + *" $1 "*) + use_strategies="$use_strategies$1 " ;; + *) + die "available strategies are: $all_strategies" ;; + esac + ;; + -m|--message) + shift + merge_msg="$1" + have_message=t + ;; + --) + shift + break ;; + *) usage ;; + esac + shift + done + args_left=$# +} + +test $# != 0 || usage + +have_message= + +if branch=$(git-symbolic-ref -q HEAD) +then + mergeopts=$(git config "branch.${branch#refs/heads/}.mergeoptions") + if test -n "$mergeopts" + then + parse_config $mergeopts -- + fi +fi + +parse_config "$@" +while test $args_left -lt $#; do shift; done + +if test -z "$show_diffstat"; then + test "$(git config --bool merge.diffstat)" = false && show_diffstat=false + test "$(git config --bool merge.stat)" = false && show_diffstat=false + test -z "$show_diffstat" && show_diffstat=t +fi + +# This could be traditional "merge HEAD ..." and the +# way we can tell it is to see if the second token is HEAD, but some +# people might have misused the interface and used a committish that +# is the same as HEAD there instead. Traditional format never would +# have "-m" so it is an additional safety measure to check for it. + +if test -z "$have_message" && + second_token=$(git rev-parse --verify "$2^0" 2>/dev/null) && + head_commit=$(git rev-parse --verify "HEAD" 2>/dev/null) && + test "$second_token" = "$head_commit" +then + merge_msg="$1" + shift + head_arg="$1" + shift +elif ! git rev-parse --verify HEAD >/dev/null 2>&1 +then + # If the merged head is a valid one there is no reason to + # forbid "git merge" into a branch yet to be born. We do + # the same for "git pull". + if test 1 -ne $# + then + echo >&2 "Can merge only exactly one commit into empty head" + exit 1 + fi + + rh=$(git rev-parse --verify "$1^0") || + die "$1 - not something we can merge" + + git update-ref -m "initial pull" HEAD "$rh" "" && + git read-tree --reset -u HEAD + exit + +else + # We are invoked directly as the first-class UI. + head_arg=HEAD + + # All the rest are the commits being merged; prepare + # the standard merge summary message to be appended to + # the given message. If remote is invalid we will die + # later in the common codepath so we discard the error + # in this loop. + merge_name=$(for remote + do + merge_name "$remote" + done | git fmt-merge-msg $log_arg + ) + merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name" +fi +head=$(git rev-parse --verify "$head_arg"^0) || usage + +# All the rest are remote heads +test "$#" = 0 && usage ;# we need at least one remote head. +set_reflog_action "merge $*" + +remoteheads= +for remote +do + remotehead=$(git rev-parse --verify "$remote"^0 2>/dev/null) || + die "$remote - not something we can merge" + remoteheads="${remoteheads}$remotehead " + eval GITHEAD_$remotehead='"$remote"' + export GITHEAD_$remotehead +done +set x $remoteheads ; shift + +case "$use_strategies" in +'') + case "$#" in + 1) + var="`git config --get pull.twohead`" + if test -n "$var" + then + use_strategies="$var" + else + use_strategies="$default_twohead_strategies" + fi ;; + *) + var="`git config --get pull.octopus`" + if test -n "$var" + then + use_strategies="$var" + else + use_strategies="$default_octopus_strategies" + fi ;; + esac + ;; +esac + +for s in $use_strategies +do + for ss in $no_fast_forward_strategies + do + case " $s " in + *" $ss "*) + allow_fast_forward=f + break + ;; + esac + done + for ss in $no_trivial_strategies + do + case " $s " in + *" $ss "*) + allow_trivial_merge=f + break + ;; + esac + done +done + +case "$#" in +1) + common=$(git merge-base --all $head "$@") + ;; +*) + common=$(git show-branch --merge-base $head "$@") + ;; +esac +echo "$head" >"$GIT_DIR/ORIG_HEAD" + +case "$allow_fast_forward,$#,$common,$no_commit" in +?,*,'',*) + # No common ancestors found. We need a real merge. + ;; +?,1,"$1",*) + # If head can reach all the merge then we are up to date. + # but first the most common case of merging one remote. + finish_up_to_date "Already up-to-date." + exit 0 + ;; +t,1,"$head",*) + # Again the most common case of merging one remote. + echo "Updating $(git rev-parse --short $head)..$(git rev-parse --short $1)" + git update-index --refresh 2>/dev/null + msg="Fast forward" + if test -n "$have_message" + then + msg="$msg (no commit created; -m option ignored)" + fi + new_head=$(git rev-parse --verify "$1^0") && + git read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" && + finish "$new_head" "$msg" || exit + dropsave + exit 0 + ;; +?,1,?*"$LF"?*,*) + # We are not doing octopus and not fast forward. Need a + # real merge. + ;; +?,1,*,) + # We are not doing octopus, not fast forward, and have only + # one common. + git update-index --refresh 2>/dev/null + case "$allow_trivial_merge" in + t) + # See if it is really trivial. + git var GIT_COMMITTER_IDENT >/dev/null || exit + echo "Trying really trivial in-index merge..." + if git read-tree --trivial -m -u -v $common $head "$1" && + result_tree=$(git write-tree) + then + echo "Wonderful." + result_commit=$( + printf '%s\n' "$merge_msg" | + git commit-tree $result_tree -p HEAD -p "$1" + ) || exit + finish "$result_commit" "In-index merge" + dropsave + exit 0 + fi + echo "Nope." + esac + ;; +*) + # An octopus. If we can reach all the remote we are up to date. + up_to_date=t + for remote + do + common_one=$(git merge-base --all $head $remote) + if test "$common_one" != "$remote" + then + up_to_date=f + break + fi + done + if test "$up_to_date" = t + then + finish_up_to_date "Already up-to-date. Yeeah!" + exit 0 + fi + ;; +esac + +# We are going to make a new commit. +git var GIT_COMMITTER_IDENT >/dev/null || exit + +# At this point, we need a real merge. No matter what strategy +# we use, it would operate on the index, possibly affecting the +# working tree, and when resolved cleanly, have the desired tree +# in the index -- this means that the index must be in sync with +# the $head commit. The strategies are responsible to ensure this. + +case "$use_strategies" in +?*' '?*) + # Stash away the local changes so that we can try more than one. + savestate + single_strategy=no + ;; +*) + rm -f "$GIT_DIR/MERGE_STASH" + single_strategy=yes + ;; +esac + +result_tree= best_cnt=-1 best_strategy= wt_strategy= +merge_was_ok= +for strategy in $use_strategies +do + test "$wt_strategy" = '' || { + echo "Rewinding the tree to pristine..." + restorestate + } + case "$single_strategy" in + no) + echo "Trying merge strategy $strategy..." + ;; + esac + + # Remember which strategy left the state in the working tree + wt_strategy=$strategy + + git-merge-$strategy $common -- "$head_arg" "$@" + exit=$? + if test "$no_commit" = t && test "$exit" = 0 + then + merge_was_ok=t + exit=1 ;# pretend it left conflicts. + fi + + test "$exit" = 0 || { + + # The backend exits with 1 when conflicts are left to be resolved, + # with 2 when it does not handle the given merge at all. + + if test "$exit" -eq 1 + then + cnt=`{ + git diff-files --name-only + git ls-files --unmerged + } | wc -l` + if test $best_cnt -le 0 -o $cnt -le $best_cnt + then + best_strategy=$strategy + best_cnt=$cnt + fi + fi + continue + } + + # Automerge succeeded. + result_tree=$(git write-tree) && break +done + +# If we have a resulting tree, that means the strategy module +# auto resolved the merge cleanly. +if test '' != "$result_tree" +then + if test "$allow_fast_forward" = "t" + then + parents=$(git show-branch --independent "$head" "$@") + else + parents=$(git rev-parse "$head" "$@") + fi + parents=$(echo "$parents" | sed -e 's/^/-p /') + result_commit=$(printf '%s\n' "$merge_msg" | git commit-tree $result_tree $parents) || exit + finish "$result_commit" "Merge made by $wt_strategy." + dropsave + exit 0 +fi + +# Pick the result from the best strategy and have the user fix it up. +case "$best_strategy" in +'') + restorestate + case "$use_strategies" in + ?*' '?*) + echo >&2 "No merge strategy handled the merge." + ;; + *) + echo >&2 "Merge with strategy $use_strategies failed." + ;; + esac + exit 2 + ;; +"$wt_strategy") + # We already have its result in the working tree. + ;; +*) + echo "Rewinding the tree to pristine..." + restorestate + echo "Using the $best_strategy to prepare resolving by hand." + git-merge-$best_strategy $common -- "$head_arg" "$@" + ;; +esac + +if test "$squash" = t +then + finish +else + for remote + do + echo $remote + done >"$GIT_DIR/MERGE_HEAD" + printf '%s\n' "$merge_msg" >"$GIT_DIR/MERGE_MSG" +fi + +if test "$merge_was_ok" = t +then + echo >&2 \ + "Automatic merge went well; stopped before committing as requested" + exit 0 +else + { + echo ' +Conflicts: +' + git ls-files --unmerged | + sed -e 's/^[^ ]* / /' | + uniq + } >>"$GIT_DIR/MERGE_MSG" + git rerere + die "Automatic merge failed; fix conflicts and then commit the result." +fi -- cgit v1.2.3 From d773c6314d5660266313772b3fd8a466c3dbc559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Tue, 8 Jul 2008 18:56:14 +0200 Subject: bash: offer only paths after '--' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many git commands use '--' to separate subcommands, options, and refs from paths. However, the programmable completion for several of these commands does not respect the '--', and offer subcommands, options, or refs after a '--', although only paths are permitted. e.g. 'git bisect -- ' offers subcommands, 'git log -- --' offers options and 'git log -- git' offers all gitgui tags. The completion for the following commands share this wrong behaviour: am add bisect commit diff log reset shortlog submodule gitk. To avoid this, we check the presence of a '--' on the command line first and let the shell do filename completion, if one is found. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0eb8df020b..cff28a88af 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -451,6 +451,18 @@ __git_find_subcommand () done } +__git_has_doubledash () +{ + local c=1 + while [ $c -lt $COMP_CWORD ]; do + if [ "--" = "${COMP_WORDS[c]}" ]; then + return 0 + fi + c=$((++c)) + done + return 1 +} + __git_whitespacelist="nowarn warn error error-all strip" _git_am () @@ -497,6 +509,8 @@ _git_apply () _git_add () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -511,6 +525,8 @@ _git_add () _git_bisect () { + __git_has_doubledash && return + local subcommands="start bad good reset visualize replay log" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then @@ -613,6 +629,8 @@ _git_cherry_pick () _git_commit () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -632,6 +650,8 @@ _git_describe () _git_diff () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -734,6 +754,8 @@ _git_ls_tree () _git_log () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --pretty=*) @@ -1085,6 +1107,8 @@ _git_remote () _git_reset () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -1097,6 +1121,8 @@ _git_reset () _git_shortlog () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) @@ -1143,6 +1169,8 @@ _git_stash () _git_submodule () { + __git_has_doubledash && return + local subcommands="add status init update" if [ -z "$(__git_find_subcommand "$subcommands")" ]; then local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1349,6 +1377,8 @@ _git () _gitk () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" local g="$(git rev-parse --git-dir 2>/dev/null)" local merge="" -- cgit v1.2.3 From 50e6102504cb211b6fe6224e67a9ed982efeb02f Mon Sep 17 00:00:00 2001 From: Eric Raible Date: Mon, 7 Jul 2008 13:41:54 -0700 Subject: completion: add branch options --contains --merged --no-merged Signed-off-by: Eric Raible Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d54aa8d62c..cc75ad7ccd 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -546,7 +546,7 @@ _git_branch () --*) __gitcomp " --color --no-color --verbose --abbrev= --no-abbrev - --track --no-track + --track --no-track --contains --merged --no-merged " ;; *) -- cgit v1.2.3 From 31a92f6aa47732e93f6685b8d1fd1ac09c1fec44 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 8 Jul 2008 19:48:04 +0200 Subject: Git.pm: Add remote_refs() git-ls-remote frontend This patch also converts the good ole' git-remote.perl to use it. It is otherwise used in the repo.or.cz machinery and I guess other scripts might find it useful too. Unfortunately, git-ls-remote --heads . is subtly different from git-ls-remote . refs/heads/ (since the second matches anywhere in the string, not just at the beginning) so we have to provide interface for both. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- contrib/examples/git-remote.perl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-remote.perl b/contrib/examples/git-remote.perl index b30ed734e7..36bd54c985 100755 --- a/contrib/examples/git-remote.perl +++ b/contrib/examples/git-remote.perl @@ -129,10 +129,7 @@ sub update_ls_remote { return if (($harder == 0) || (($harder == 1) && exists $info->{'LS_REMOTE'})); - my @ref = map { - s|^[0-9a-f]{40}\s+refs/heads/||; - $_; - } $git->command(qw(ls-remote --heads), $info->{'URL'}); + my @ref = map { s|refs/heads/||; $_; } keys %{$git->remote_refs($info->{'URL'}, [ 'heads' ])}; $info->{'LS_REMOTE'} = \@ref; } -- cgit v1.2.3 From ab02dfe533f55535bdb66e05776a4081020322c6 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 13 Jul 2008 02:37:42 +0000 Subject: bash completion: Improve responsiveness of git-log completion Junio noticed the bash completion has been taking a long time lately. Petr Baudis tracked it down to 72e5e989b ("bash: Add space after unique command name is completed."). Tracing the code showed we spent significant time inside of this loop within __gitcomp, due to the string copying overhead. [28.146109654] _git common over [28.164791148] gitrefs in [28.280302268] gitrefs dir out [28.300939737] gitcomp in [28.308378112] gitcomp pre-case * [28.313407453] gitcomp iter in * [28.701270296] gitcomp iter out [28.713370786] out normal Since __git_refs avoids this string copying by forking and using echo we use the same trick here when we need to finish generating the names for the caller. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index cff28a88af..0734ea313c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -114,9 +114,20 @@ __git_ps1 () fi } +__gitcomp_1 () +{ + local c IFS=' '$'\t'$'\n' + for c in $1; do + case "$c$2" in + --*=*) printf %s$'\n' "$c$2" ;; + *.) printf %s$'\n' "$c$2" ;; + *) printf %s$'\n' "$c$2 " ;; + esac + done +} + __gitcomp () { - local all c s=$'\n' IFS=' '$'\t'$'\n' local cur="${COMP_WORDS[COMP_CWORD]}" if [ $# -gt 2 ]; then cur="$3" @@ -124,21 +135,14 @@ __gitcomp () case "$cur" in --*=) COMPREPLY=() - return ;; *) - for c in $1; do - case "$c$4" in - --*=*) all="$all$c$4$s" ;; - *.) all="$all$c$4$s" ;; - *) all="$all$c$4 $s" ;; - esac - done + local IFS=$'\n' + COMPREPLY=($(compgen -P "$2" \ + -W "$(__gitcomp_1 "$1" "$4")" \ + -- "$cur")) ;; esac - IFS=$s - COMPREPLY=($(compgen -P "$2" -W "$all" -- "$cur")) - return } __git_heads () -- cgit v1.2.3 From 6c36c9e4eabadecf75f8751b1c1140da2068e2a0 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 13 Jul 2008 22:06:31 +0000 Subject: bash completion: Don't offer "a.." as a completion for "a." If the user is trying to complete "v1.5.3." to see all of the available maintenance releases for 1.5.3 we should not give them an extra dot as the completion. Instead if the user wants a ".." or a "..." operator they should key the two dots out on their own. Its the same number of keystrokes either way. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 --- 1 file changed, 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0734ea313c..821c9a7f9f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -324,9 +324,6 @@ __git_complete_revlist () cur="${cur#*..}" __gitcomp "$(__git_refs)" "$pfx" "$cur" ;; - *.) - __gitcomp "$cur." - ;; *) __gitcomp "$(__git_refs)" ;; -- cgit v1.2.3 From 778306e405b416d8073652c535777f2de5fc68ad Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Mon, 14 Jul 2008 00:22:03 +0000 Subject: bash completion: Append space after file names have been completed When completing `git show origin/maint:Makef` we should add a space after the filename has been completed, so that the user can immediately begin the next argument. I also added a special case for the symlink variant so we treat it just like a normal blob, as there are no items below it in the Git tree structure. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 821c9a7f9f..0a3bea44f7 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -294,9 +294,17 @@ __git_complete_file () ls="$ref" ;; esac + local IFS=$'\n' COMPREPLY=($(compgen -P "$pfx" \ -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \ - | sed '/^100... blob /s,^.* ,, + | sed '/^100... blob /{ + s,^.* ,, + s,$, , + } + /^120000 blob /{ + s,^.* ,, + s,$, , + } /^040000 tree /{ s,^.* ,, s,$,/, -- cgit v1.2.3 From db8a9ff03831a26aa8bfad8bb026b90739d684ec Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 15 Jul 2008 05:52:04 +0000 Subject: bash completion: Resolve git show ref:path losing ref: portion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linus reported that the bash completion for git show often dropped the ref portion of the argument (stuff before the :) when trying to complete a file name of a file in another branch or tag. Björn Steinbrink tracked it down to the gvfs completion script which comes standard on many Fedora Core based systems. That is removing : from COMP_WORDBREAKS, making readline treat the entire argument (including the ref) as the name that must be completed. When the git completion routines supplied a completion of just the filename, readline replaced everything. Since Git users often need to use "ref:path" or "ref:ref" sort of arguments, and expect completion support on both sides of the : we really want the : in COMP_WORDBREAKS to provide a good user experience. This is also the default that ships with bash as it can be useful in other contexts, such as rcp/scp. We now try to add : back to COMP_WORDBREAKS if it has been removed by a script that loaded before us. However if this doesn't work (as the : is stripped after we load) we fallback in the completion routines to include "ref:" as part of the prefix for completions, allowing readine to fully insert the argument the user wanted. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0a3bea44f7..72f02f208f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -45,6 +45,11 @@ # git@vger.kernel.org # +case "$COMP_WORDBREAKS" in +*:*) : great ;; +*) COMP_WORDBREAKS="$COMP_WORDBREAKS:" +esac + __gitdir () { if [ -z "$1" ]; then @@ -294,6 +299,12 @@ __git_complete_file () ls="$ref" ;; esac + + case "$COMP_WORDBREAKS" in + *:*) : great ;; + *) pfx="$ref:$pfx" ;; + esac + local IFS=$'\n' COMPREPLY=($(compgen -P "$pfx" \ -W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \ @@ -700,7 +711,12 @@ _git_fetch () *) case "$cur" in *:*) - __gitcomp "$(__git_refs)" "" "${cur#*:}" + local pfx="" + case "$COMP_WORDBREAKS" in + *:*) : great ;; + *) pfx="${cur%%:*}:" ;; + esac + __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}" ;; *) local remote @@ -873,7 +889,14 @@ _git_push () git-push) remote="${COMP_WORDS[1]}" ;; git) remote="${COMP_WORDS[2]}" ;; esac - __gitcomp "$(__git_refs "$remote")" "" "${cur#*:}" + + local pfx="" + case "$COMP_WORDBREAKS" in + *:*) : great ;; + *) pfx="${cur%%:*}:" ;; + esac + + __gitcomp "$(__git_refs "$remote")" "$pfx" "${cur#*:}" ;; +*) __gitcomp "$(__git_refs)" + "${cur#+}" -- cgit v1.2.3 From 25a1f374f0ff23a4d9191436226ab68f3da5e83a Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Mon, 14 Jul 2008 11:21:02 +0300 Subject: bash: Add long option completion for 'git send-email' Add the following long options to be completed with 'git send-email': --bcc --cc --cc-cmd --chain-reply-to --compose --dry-run --envelope-sender --from --identity --in-reply-to --no-chain-reply-to --no-signed-off-by-cc --no-suppress-from --no-thread --quiet --signed-off-by-cc --smtp-pass --smtp-server --smtp-server-port --smtp-ssl --smtp-user --subject --suppress-cc --suppress-from --thread --to Short ones like --to and --cc are not usable for actual completion because of the shortness itself and because there are longer ones which start with same letters (--thread, --compose). It's still useful to have these shorter options _listed_ when user presses TAB key after typing two dashes. It gives user an idea what options are available (and --to and --cc are probably the most commonly used). Signed-off-by: Teemu Likonen Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d268e6f0b3..d48dbf2672 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -905,6 +905,24 @@ _git_rebase () __gitcomp "$(__git_refs)" } +_git_send_email () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--bcc --cc --cc-cmd --chain-reply-to --compose + --dry-run --envelope-sender --from --identity + --in-reply-to --no-chain-reply-to --no-signed-off-by-cc + --no-suppress-from --no-thread --quiet + --signed-off-by-cc --smtp-pass --smtp-server + --smtp-server-port --smtp-ssl --smtp-user --subject + --suppress-cc --suppress-from --thread --to" + return + ;; + esac + COMPREPLY=() +} + _git_config () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1376,6 +1394,7 @@ _git () rebase) _git_rebase ;; remote) _git_remote ;; reset) _git_reset ;; + send-email) _git_send_email ;; shortlog) _git_shortlog ;; show) _git_show ;; show-branch) _git_log ;; @@ -1435,6 +1454,7 @@ complete -o default -o nospace -F _git_rebase git-rebase complete -o default -o nospace -F _git_config git-config complete -o default -o nospace -F _git_remote git-remote complete -o default -o nospace -F _git_reset git-reset +complete -o default -o nospace -F _git_send_email git-send-email complete -o default -o nospace -F _git_shortlog git-shortlog complete -o default -o nospace -F _git_show git-show complete -o default -o nospace -F _git_stash git-stash -- cgit v1.2.3 From 055767194c15f9b7c39a509daf1a77472c77bed8 Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Tue, 15 Jul 2008 05:58:06 +0000 Subject: bash completion: Remove dashed command completion support Since only 'git' and 'gitk' are in the user's $PATH now we do not expect users to need completion support for git-fetch, and expect they will instead rely upon the completion support for 'git fetch'. Signed-off-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 54 ---------------------------------- 1 file changed, 54 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 37f52d5395..03e4e02785 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1459,65 +1459,11 @@ _gitk () complete -o default -o nospace -F _git git complete -o default -o nospace -F _gitk gitk -complete -o default -o nospace -F _git_am git-am -complete -o default -o nospace -F _git_apply git-apply -complete -o default -o nospace -F _git_bisect git-bisect -complete -o default -o nospace -F _git_branch git-branch -complete -o default -o nospace -F _git_bundle git-bundle -complete -o default -o nospace -F _git_checkout git-checkout -complete -o default -o nospace -F _git_cherry git-cherry -complete -o default -o nospace -F _git_cherry_pick git-cherry-pick -complete -o default -o nospace -F _git_commit git-commit -complete -o default -o nospace -F _git_describe git-describe -complete -o default -o nospace -F _git_diff git-diff -complete -o default -o nospace -F _git_fetch git-fetch -complete -o default -o nospace -F _git_format_patch git-format-patch -complete -o default -o nospace -F _git_gc git-gc -complete -o default -o nospace -F _git_log git-log -complete -o default -o nospace -F _git_ls_remote git-ls-remote -complete -o default -o nospace -F _git_ls_tree git-ls-tree -complete -o default -o nospace -F _git_merge git-merge -complete -o default -o nospace -F _git_merge_base git-merge-base -complete -o default -o nospace -F _git_name_rev git-name-rev -complete -o default -o nospace -F _git_pull git-pull -complete -o default -o nospace -F _git_push git-push -complete -o default -o nospace -F _git_rebase git-rebase -complete -o default -o nospace -F _git_config git-config -complete -o default -o nospace -F _git_remote git-remote -complete -o default -o nospace -F _git_reset git-reset -complete -o default -o nospace -F _git_send_email git-send-email -complete -o default -o nospace -F _git_shortlog git-shortlog -complete -o default -o nospace -F _git_show git-show -complete -o default -o nospace -F _git_stash git-stash -complete -o default -o nospace -F _git_submodule git-submodule -complete -o default -o nospace -F _git_svn git-svn -complete -o default -o nospace -F _git_log git-show-branch -complete -o default -o nospace -F _git_tag git-tag -complete -o default -o nospace -F _git_log git-whatchanged # The following are necessary only for Cygwin, and only are needed # when the user has tab-completed the executable name and consequently # included the '.exe' suffix. # if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then -complete -o default -o nospace -F _git_add git-add.exe -complete -o default -o nospace -F _git_apply git-apply.exe complete -o default -o nospace -F _git git.exe -complete -o default -o nospace -F _git_branch git-branch.exe -complete -o default -o nospace -F _git_bundle git-bundle.exe -complete -o default -o nospace -F _git_cherry git-cherry.exe -complete -o default -o nospace -F _git_describe git-describe.exe -complete -o default -o nospace -F _git_diff git-diff.exe -complete -o default -o nospace -F _git_format_patch git-format-patch.exe -complete -o default -o nospace -F _git_log git-log.exe -complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe -complete -o default -o nospace -F _git_merge_base git-merge-base.exe -complete -o default -o nospace -F _git_name_rev git-name-rev.exe -complete -o default -o nospace -F _git_push git-push.exe -complete -o default -o nospace -F _git_config git-config -complete -o default -o nospace -F _git_shortlog git-shortlog.exe -complete -o default -o nospace -F _git_show git-show.exe -complete -o default -o nospace -F _git_log git-show-branch.exe -complete -o default -o nospace -F _git_tag git-tag.exe -complete -o default -o nospace -F _git_log git-whatchanged.exe fi -- cgit v1.2.3 From 28ed6e7b321bee3dd7e4aa9c0ff7da64844136f6 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 16 Jul 2008 03:33:44 +0200 Subject: Rename ".dotest/" to ".git/rebase" and ".dotest-merge" to "rebase-merge" Since the files generated and used during a rebase are never to be tracked, they should live in $GIT_DIR. While at it, avoid the rather meaningless term "dotest" to "rebase", and unhide ".dotest-merge". This was wished for on the mailing list, but so far unimplemented. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 20 ++++++++++---------- contrib/emacs/git.el | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 03e4e02785..29f6cd4e9e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -73,26 +73,26 @@ __git_ps1 () if [ -n "$g" ]; then local r local b - if [ -d "$g/../.dotest" ] + if [ -d "$g/rebase" ] then - if test -f "$g/../.dotest/rebasing" + if test -f "$g/rebase/rebasing" then r="|REBASE" - elif test -f "$g/../.dotest/applying" + elif test -f "$g/rebase/applying" then r="|AM" else r="|AM/REBASE" fi b="$(git symbolic-ref HEAD 2>/dev/null)" - elif [ -f "$g/.dotest-merge/interactive" ] + elif [ -f "$g/rebase-merge/interactive" ] then r="|REBASE-i" - b="$(cat "$g/.dotest-merge/head-name")" - elif [ -d "$g/.dotest-merge" ] + b="$(cat "$g/rebase-merge/head-name")" + elif [ -d "$g/rebase-merge" ] then r="|REBASE-m" - b="$(cat "$g/.dotest-merge/head-name")" + b="$(cat "$g/rebase-merge/head-name")" elif [ -f "$g/MERGE_HEAD" ] then r="|MERGING" @@ -487,8 +487,8 @@ __git_whitespacelist="nowarn warn error error-all strip" _git_am () { - local cur="${COMP_WORDS[COMP_CWORD]}" - if [ -d .dotest ]; then + local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" + if [ -d "$dir"/rebase ]; then __gitcomp "--skip --resolved" return fi @@ -915,7 +915,7 @@ _git_push () _git_rebase () { local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" - if [ -d .dotest ] || [ -d "$dir"/.dotest-merge ]; then + if [ -d "$dir"/rebase ] || [ -d "$dir"/rebase-merge ]; then __gitcomp "--continue --skip --abort" return fi diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 4fa853fae7..43b059bbaa 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1252,8 +1252,8 @@ Return the list of files that haven't been handled." "\n") (when subject (insert subject "\n\n")) (cond (msg (insert msg "\n")) - ((file-readable-p ".dotest/msg") - (insert-file-contents ".dotest/msg")) + ((file-readable-p ".git/rebase/msg") + (insert-file-contents ".git/rebase/msg")) ((file-readable-p ".git/MERGE_MSG") (insert-file-contents ".git/MERGE_MSG"))) ; delete empty lines at end @@ -1272,9 +1272,9 @@ Return the list of files that haven't been handled." (coding-system (git-get-commits-coding-system)) author-name author-email subject date) (when (eq 0 (buffer-size buffer)) - (when (file-readable-p ".dotest/info") + (when (file-readable-p ".git/rebase/info") (with-temp-buffer - (insert-file-contents ".dotest/info") + (insert-file-contents ".git/rebase/info") (goto-char (point-min)) (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) (setq author-name (match-string 1)) -- cgit v1.2.3 From 51ef1daa4a0dfaa4d777b2fa949ba051cf800554 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 21 Jul 2008 12:51:02 +0200 Subject: Rename .git/rebase to .git/rebase-apply With git-am, it sounds awkward to have the patches in ".git/rebase/", but for technical reasons, we have to keep the same directory name for git-am and git-rebase. ".git/rebase-apply" seems to be a good compromise. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 +++++----- contrib/emacs/git.el | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 29f6cd4e9e..2edb341b57 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -73,12 +73,12 @@ __git_ps1 () if [ -n "$g" ]; then local r local b - if [ -d "$g/rebase" ] + if [ -d "$g/rebase-apply" ] then - if test -f "$g/rebase/rebasing" + if test -f "$g/rebase-apply/rebasing" then r="|REBASE" - elif test -f "$g/rebase/applying" + elif test -f "$g/rebase-apply/applying" then r="|AM" else @@ -488,7 +488,7 @@ __git_whitespacelist="nowarn warn error error-all strip" _git_am () { local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" - if [ -d "$dir"/rebase ]; then + if [ -d "$dir"/rebase-apply ]; then __gitcomp "--skip --resolved" return fi @@ -915,7 +915,7 @@ _git_push () _git_rebase () { local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" - if [ -d "$dir"/rebase ] || [ -d "$dir"/rebase-merge ]; then + if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then __gitcomp "--continue --skip --abort" return fi diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 43b059bbaa..c1cf1cbcc0 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1252,8 +1252,8 @@ Return the list of files that haven't been handled." "\n") (when subject (insert subject "\n\n")) (cond (msg (insert msg "\n")) - ((file-readable-p ".git/rebase/msg") - (insert-file-contents ".git/rebase/msg")) + ((file-readable-p ".git/rebase-apply/msg") + (insert-file-contents ".git/rebase-apply/msg")) ((file-readable-p ".git/MERGE_MSG") (insert-file-contents ".git/MERGE_MSG"))) ; delete empty lines at end @@ -1272,9 +1272,9 @@ Return the list of files that haven't been handled." (coding-system (git-get-commits-coding-system)) author-name author-email subject date) (when (eq 0 (buffer-size buffer)) - (when (file-readable-p ".git/rebase/info") + (when (file-readable-p ".git/rebase-apply/info") (with-temp-buffer - (insert-file-contents ".git/rebase/info") + (insert-file-contents ".git/rebase-apply/info") (goto-char (point-min)) (when (re-search-forward "^Author: \\(.*\\)\nEmail: \\(.*\\)$" nil t) (setq author-name (match-string 1)) -- cgit v1.2.3 From c79cc2e59672fa03985f27ecdbea84e06ea5358f Mon Sep 17 00:00:00 2001 From: Nikolaj Schumacher Date: Mon, 30 Jun 2008 12:08:16 +0200 Subject: Don't cut off last character of commit descriptions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should have been part of 24a2293 (git-blame.el: show the when, who and what in the minibuffer., 2008-02-12), that changed from using --pretty=oneline to --pretty=format:... without terminating newline. Acked-by: David Kågedal Signed-off-by: Junio C Hamano --- contrib/emacs/git-blame.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el index 9f92cd250b..4fa70c5ad4 100644 --- a/contrib/emacs/git-blame.el +++ b/contrib/emacs/git-blame.el @@ -381,7 +381,7 @@ See also function `git-blame-mode'." "log" "-1" (concat "--pretty=" git-blame-log-oneline-format) hash) - (buffer-substring (point-min) (1- (point-max))))) + (buffer-substring (point-min) (point-max)))) (defvar git-blame-last-identification nil) (make-variable-buffer-local 'git-blame-last-identification) -- cgit v1.2.3 From 2d71bde2d1a8f4e1c06cb8049a794ba4ba35d37d Mon Sep 17 00:00:00 2001 From: Daniel Barkalow Date: Tue, 22 Jul 2008 12:48:57 -0400 Subject: In perforce, RCS keywords are case-sensitive At least, this is true in 2007.2, according to the documentation. Signed-off-by: Daniel Barkalow Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 87ca51e401..6ae0429c2d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -906,7 +906,7 @@ class P4Sync(Command): if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text) elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): - text = re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text) + text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text) contents[stat['depotFile']] = text -- cgit v1.2.3 From a31c00b00eeadbc4b87ce2d578688e9b369a50fe Mon Sep 17 00:00:00 2001 From: Stephan Beyer Date: Wed, 23 Jul 2008 02:10:25 +0200 Subject: am --abort: Add to bash-completion and mention in git-rerere documentation The git-rerere documentation talks about commands that invoke "git rerere clear" automatically. git am --abort is added and a typo is fixed additionally. Signed-off-by: Stephan Beyer Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2edb341b57..8fc9145282 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -489,7 +489,7 @@ _git_am () { local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)" if [ -d "$dir"/rebase-apply ]; then - __gitcomp "--skip --resolved" + __gitcomp "--skip --resolved --abort" return fi case "$cur" in -- cgit v1.2.3 From e8a43a132d97165f6e56fa03923a3933cfedde81 Mon Sep 17 00:00:00 2001 From: "P. Christeas" Date: Wed, 23 Jul 2008 23:08:27 +0300 Subject: svnimport: newer libsvn wants us to ask for the root with "", not "/" In r27729, libsvn introduced an assert which explicitly forbids searching the tree at "/". Luckily enough, it still accepts an empty string "" as the starting point. http://svn.collab.net/viewvc/svn/trunk/subversion/libsvn_ra/ra_loader.c?r1=27653&r2=27729 Tested against libsvn0-1.5.0-4mdv2009.0 (needs the fix), libsvn0-1.4.6-5mdv2008.1 (works anyway) Signed-off-by: P. Christeas Signed-off-by: Junio C Hamano --- contrib/examples/git-svnimport.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl index ea8c1b2f60..a13bb6afec 100755 --- a/contrib/examples/git-svnimport.perl +++ b/contrib/examples/git-svnimport.perl @@ -933,7 +933,7 @@ while ($to_rev < $opt_l) { $to_rev = $from_rev + $repack_after; $to_rev = $opt_l if $opt_l < $to_rev; print "Fetching from $from_rev to $to_rev ...\n" if $opt_v; - $svn->{'svn'}->get_log("/",$from_rev,$to_rev,0,1,1,\&commit_all); + $svn->{'svn'}->get_log("",$from_rev,$to_rev,0,1,1,\&commit_all); my $pid = fork(); die "Fork: $!\n" unless defined $pid; unless($pid) { -- cgit v1.2.3 From 08c701d4761abf58dce607e84bf41fb280e38a9e Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 23 Jul 2008 15:21:08 -0600 Subject: bash completion: Add long options for 'git rm' Options added: --cached --dry-run --ignore-unmatch --quiet Signed-off-by: Lee Marlow Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8fc9145282..e20d57a1ba 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1170,6 +1170,20 @@ _git_reset () __gitcomp "$(__git_refs)" } +_git_rm () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --dry-run --ignore-unmatch --quiet" + return + ;; + esac + COMPREPLY=() +} + _git_shortlog () { __git_has_doubledash && return @@ -1425,6 +1439,7 @@ _git () rebase) _git_rebase ;; remote) _git_remote ;; reset) _git_reset ;; + rm) _git_rm ;; send-email) _git_send_email ;; shortlog) _git_shortlog ;; show) _git_show ;; -- cgit v1.2.3 From 2ca880fe54660869bc93a2302efced9ab64511d9 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Wed, 23 Jul 2008 23:36:15 +0200 Subject: git-completion.bash: provide completion for 'show-branch' It previously used the same as 'log', but the options are quite different and the arguments must be single refs (or globs). Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e20d57a1ba..3b049348c3 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1224,6 +1224,22 @@ _git_show () __git_complete_file } +_git_show_branch () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --all --remotes --topo-order --current --more= + --list --independent --merge-base --no-name + --sha1-name --topics --reflog + " + return + ;; + esac + __git_complete_revlist +} + _git_stash () { local subcommands='save list show apply clear drop pop create' @@ -1443,7 +1459,7 @@ _git () send-email) _git_send_email ;; shortlog) _git_shortlog ;; show) _git_show ;; - show-branch) _git_log ;; + show-branch) _git_show_branch ;; stash) _git_stash ;; submodule) _git_submodule ;; svn) _git_svn ;; -- cgit v1.2.3 From c84bb14ce52b6559e0b8e10d554ff9b47149c042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Wed, 23 Jul 2008 13:49:22 +0200 Subject: bash: offer only paths after '--' for 'git checkout' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit d773c631 (bash: offer only paths after '--', 2008-07-08) did the same for several other git commands, but 'git checkout' went unnoticed. Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3b049348c3..40b3d99737 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -626,6 +626,8 @@ _git_bundle () _git_checkout () { + __git_has_doubledash && return + __gitcomp "$(__git_refs)" } -- cgit v1.2.3 From cbb504c97437653540dc55430a6f64da9ddd24fd Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Sat, 26 Jul 2008 12:26:56 +0200 Subject: bash completion: Add long options for 'git describe' Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 40b3d99737..2fb88a8fef 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -667,6 +667,15 @@ _git_commit () _git_describe () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --all --tags --contains --abbrev= --candidates= + --exact-match --debug --long --match --always + " + return + esac __gitcomp "$(__git_refs)" } -- cgit v1.2.3 From 1eb7e2f83472f49fda62cefe1d2d9b4c668c6771 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 23 Jul 2008 18:07:23 -0600 Subject: bash completion: Add completion for 'git help' Rename cached __git_commandlist to __git_porcelain_commandlist and add __git_all_commandlist that only filters out *--* helpers. Completions for 'git help' will use the __git_all_commandlist, while the __git_porcelain_commandlist is used for git command completion. Users who actually read man pages may want to see help for plumbing commands. Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 46 ++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2fb88a8fef..30d870187e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -349,14 +349,32 @@ __git_complete_revlist () esac } -__git_commands () +__git_all_commands () { - if [ -n "$__git_commandlist" ]; then - echo "$__git_commandlist" + if [ -n "$__git_all_commandlist" ]; then + echo "$__git_all_commandlist" return fi local i IFS=" "$'\n' for i in $(git help -a|egrep '^ ') + do + case $i in + *--*) : helper pattern;; + *) echo $i;; + esac + done +} +__git_all_commandlist= +__git_all_commandlist="$(__git_all_commands 2>/dev/null)" + +__git_porcelain_commands () +{ + if [ -n "$__git_porcelain_commandlist" ]; then + echo "$__git_porcelain_commandlist" + return + fi + local i IFS=" "$'\n' + for i in "help" $(__git_all_commands) do case $i in *--*) : helper pattern;; @@ -427,8 +445,8 @@ __git_commands () esac done } -__git_commandlist= -__git_commandlist="$(__git_commands 2>/dev/null)" +__git_porcelain_commandlist= +__git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)" __git_aliases () { @@ -778,6 +796,18 @@ _git_gc () COMPREPLY=() } +_git_help () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--all --info --man --web" + return + ;; + esac + __gitcomp "$(__git_all_commands)" +} + _git_ls_remote () { __gitcomp "$(__git_remotes)" @@ -1410,7 +1440,8 @@ _git () case "$i" in --git-dir=*) __git_dir="${i#--git-dir=}" ;; --bare) __git_dir="." ;; - --version|--help|-p|--paginate) ;; + --version|-p|--paginate) ;; + --help) command="help"; break ;; *) command="$i"; break ;; esac c=$((++c)) @@ -1430,7 +1461,7 @@ _git () --help " ;; - *) __gitcomp "$(__git_commands) $(__git_aliases)" ;; + *) __gitcomp "$(__git_porcelain_commands) $(__git_aliases)" ;; esac return fi @@ -1455,6 +1486,7 @@ _git () fetch) _git_fetch ;; format-patch) _git_format_patch ;; gc) _git_gc ;; + help) _git_help ;; log) _git_log ;; ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; -- cgit v1.2.3 From 5354a56fe70420c147f930e0f7f1decbae685d19 Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Wed, 30 Jul 2008 13:48:33 -0400 Subject: Replace uses of "git-var" with "git var" Signed-off-by: Todd Zullinger Signed-off-by: Junio C Hamano --- contrib/examples/git-commit.sh | 6 +++--- contrib/examples/git-tag.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-commit.sh b/contrib/examples/git-commit.sh index 2c4a4062a5..5c72f655c7 100755 --- a/contrib/examples/git-commit.sh +++ b/contrib/examples/git-commit.sh @@ -443,7 +443,7 @@ fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG case "$signoff" in t) - sign=$(git-var GIT_COMMITTER_IDENT | sed -e ' + sign=$(git var GIT_COMMITTER_IDENT | sed -e ' s/>.*/>/ s/^/Signed-off-by: / ') @@ -535,8 +535,8 @@ esac case "$no_edit" in '') - git-var GIT_AUTHOR_IDENT > /dev/null || die - git-var GIT_COMMITTER_IDENT > /dev/null || die + git var GIT_AUTHOR_IDENT > /dev/null || die + git var GIT_COMMITTER_IDENT > /dev/null || die git_editor "$GIT_DIR/COMMIT_EDITMSG" ;; esac diff --git a/contrib/examples/git-tag.sh b/contrib/examples/git-tag.sh index e9f3a228af..2c15bc955b 100755 --- a/contrib/examples/git-tag.sh +++ b/contrib/examples/git-tag.sh @@ -164,7 +164,7 @@ git check-ref-format "tags/$name" || object=$(git rev-parse --verify --default HEAD "$@") || exit 1 type=$(git cat-file -t $object) || exit 1 -tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1 +tagger=$(git var GIT_COMMITTER_IDENT) || exit 1 test -n "$username" || username=$(git config user.signingkey) || -- cgit v1.2.3 From 7339479c2b2986891763300e385e71ab6ae56322 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Fri, 1 Aug 2008 22:47:09 -0600 Subject: bash completion: remove unused function _git_diff_tree completion for git diff-tree was removed in 5cfb4fe Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 ----- 1 file changed, 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 30d870187e..e32c1f1a9c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -721,11 +721,6 @@ _git_diff () __git_complete_file } -_git_diff_tree () -{ - __gitcomp "$(__git_refs)" -} - _git_fetch () { local cur="${COMP_WORDS[COMP_CWORD]}" -- cgit v1.2.3 From e49b99a6f5ac9f6638b0e5f938674f031555f374 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Fri, 1 Aug 2008 18:56:53 -0600 Subject: bash completion: Add more long options for 'git log' Options added: --parents --children --full-history Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e32c1f1a9c..678a155f2e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -848,6 +848,7 @@ _git_log () --stat --numstat --shortstat --decorate --diff-filter= --color-words --walk-reflogs + --parents --children --full-history " return ;; -- cgit v1.2.3 From c72e0db1ff686e93478ee21a3e80a9ca73143753 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Fri, 1 Aug 2008 18:56:33 -0600 Subject: bash completion: Add completion for 'git grep' Add completions for all long options specified in the docs --cached --text --ignore-case --word-regexp --invert-match --full-name --extended-regexp --basic-regexp --fixed-strings --files-with-matches --name-only --files-without-match --count --and --or --not --all-match Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 678a155f2e..253be56ae2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -791,6 +791,29 @@ _git_gc () COMPREPLY=() } +_git_grep () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --cached + --text --ignore-case --word-regexp --invert-match + --full-name + --extended-regexp --basic-regexp --fixed-strings + --files-with-matches --name-only + --files-without-match + --count + --and --or --not --all-match + " + return + ;; + esac + COMPREPLY=() +} + _git_help () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1482,6 +1505,7 @@ _git () fetch) _git_fetch ;; format-patch) _git_format_patch ;; gc) _git_gc ;; + grep) _git_grep ;; help) _git_help ;; log) _git_log ;; ls-remote) _git_ls_remote ;; -- cgit v1.2.3 From 3eb11012078e9a2b9f444dbf1aae1f1cdd33fef1 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:31 -0600 Subject: bash completion: Add completion for 'git clone' Add completions for all long options specified in the docs --local --no-hardlinks --shared --reference --quiet --no-checkout --bare --mirror --origin --upload-pack --template= --depth Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 253be56ae2..1474525283 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -667,6 +667,31 @@ _git_cherry_pick () esac } +_git_clone () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --local + --no-hardlinks + --shared + --reference + --quiet + --no-checkout + --bare + --mirror + --origin + --upload-pack + --template= + --depth + " + return + ;; + esac + COMPREPLY=() +} + _git_commit () { __git_has_doubledash && return @@ -1498,6 +1523,7 @@ _git () checkout) _git_checkout ;; cherry) _git_cherry ;; cherry-pick) _git_cherry_pick ;; + clone) _git_clone ;; commit) _git_commit ;; config) _git_config ;; describe) _git_describe ;; -- cgit v1.2.3 From 4181c7e8a7ece0cf117e7ec6bb8bca989e116f7e Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:32 -0600 Subject: bash completion: Add completion for 'git clean' Add completions for all long options specified in the docs --dry-run --quiet Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1474525283..97ece7d8e5 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -667,6 +667,20 @@ _git_cherry_pick () esac } +_git_clean () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--dry-run --quiet" + return + ;; + esac + COMPREPLY=() +} + _git_clone () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1523,6 +1537,7 @@ _git () checkout) _git_checkout ;; cherry) _git_cherry ;; cherry-pick) _git_cherry_pick ;; + clean) _git_clean ;; clone) _git_clone ;; commit) _git_commit ;; config) _git_config ;; -- cgit v1.2.3 From 5dad868b26919b03dd78fba182a152224a904d3a Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:33 -0600 Subject: bash completion: Add completion for 'git init' Add completions for all long options specified in the docs --quiet --bare --template= --shared --shared={false|true|umask|group|all|world|everybody} Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 97ece7d8e5..a6d8d0c03e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -865,6 +865,24 @@ _git_help () __gitcomp "$(__git_all_commands)" } +_git_init () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --shared=*) + __gitcomp " + false true umask group all world everybody + " "" "${cur##--shared=}" + return + ;; + --*) + __gitcomp "--quiet --bare --template= --shared --shared=" + return + ;; + esac + COMPREPLY=() +} + _git_ls_remote () { __gitcomp "$(__git_remotes)" @@ -1548,6 +1566,7 @@ _git () gc) _git_gc ;; grep) _git_grep ;; help) _git_help ;; + init) _git_init ;; log) _git_log ;; ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; -- cgit v1.2.3 From a6c2be244f45432617944e684156556482fb11b6 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:34 -0600 Subject: bash completion: Add completion for 'git revert' Add completions for all long options specified in the docs --edit --mainline --no-edit --no-commit --signoff Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a6d8d0c03e..265842712e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1287,6 +1287,18 @@ _git_reset () __gitcomp "$(__git_refs)" } +_git_revert () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--edit --mainline --no-edit --no-commit --signoff" + return + ;; + esac + COMPREPLY=() +} + _git_rm () { __git_has_doubledash && return @@ -1578,6 +1590,7 @@ _git () rebase) _git_rebase ;; remote) _git_remote ;; reset) _git_reset ;; + revert) _git_revert ;; rm) _git_rm ;; send-email) _git_send_email ;; shortlog) _git_shortlog ;; -- cgit v1.2.3 From 95d43780cff575cfffeea881464c77f84f8d7244 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:35 -0600 Subject: bash completion: More completions for 'git stash' Add branch subcommand to completions and USAGE for git-stash.sh. Complete stash names for show, apply, drop, pop, and branch. Add "--index" long option for apply. Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 265842712e..49a03c314e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1371,7 +1371,7 @@ _git_show_branch () _git_stash () { - local subcommands='save list show apply clear drop pop create' + local subcommands='save list show apply clear drop pop create branch' local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" @@ -1381,6 +1381,16 @@ _git_stash () save,--*) __gitcomp "--keep-index" ;; + apply,--*) + __gitcomp "--index" + ;; + show,--*|apply,--*|drop,--*|pop,--*|branch,--*) + COMPREPLY=() + ;; + show,*|apply,*|drop,*|pop,*|branch,*) + __gitcomp "$(git --git-dir="$(__gitdir)" stash list \ + | sed -n -e 's/:.*//p')" + ;; *) COMPREPLY=() ;; -- cgit v1.2.3 From b3191ce2d5a77c17e5a236823f2dc7393e440171 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:36 -0600 Subject: bash completion: Add completion for 'git archive' Add completions for all long options specified in the docs --format= --list --verbose --prefix= --remote= --exec= The --format= long option can be completed with available formats and the --remote= can be completed with defined remote repositories. Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 49a03c314e..3209e5c4b1 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -561,6 +561,29 @@ _git_add () COMPREPLY=() } +_git_archive () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --format=*) + __gitcomp "$(git archive --list)" "" "${cur##--format=}" + return + ;; + --remote=*) + __gitcomp "$(__git_remotes)" "" "${cur##--remote=}" + return + ;; + --*) + __gitcomp " + --format= --list --verbose + --prefix= --remote= --exec= + " + return + ;; + esac + __git_complete_file +} + _git_bisect () { __git_has_doubledash && return @@ -1571,6 +1594,7 @@ _git () am) _git_am ;; add) _git_add ;; apply) _git_apply ;; + archive) _git_archive ;; bisect) _git_bisect ;; bundle) _git_bundle ;; branch) _git_branch ;; -- cgit v1.2.3 From b1bc1494edb1775e94a159dea67908b27c29017f Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:37 -0600 Subject: bash completion: Add completion for 'git ls-files' Add completions for all long options specified in the docs --cached --deleted --modified --others --ignored --stage --directory --no-empty-directory --unmerged --killed --exclude= --exclude-from= --exclude-per-directory= --exclude-standard --error-unmatch --with-tree= --full-name --abbrev --ignored --exclude-per-directory Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3209e5c4b1..7a7bc99955 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -906,6 +906,26 @@ _git_init () COMPREPLY=() } +_git_ls_files () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --deleted --modified --others --ignored + --stage --directory --no-empty-directory --unmerged + --killed --exclude= --exclude-from= + --exclude-per-directory= --exclude-standard + --error-unmatch --with-tree= --full-name + --abbrev --ignored --exclude-per-directory + " + return + ;; + esac + COMPREPLY=() +} + _git_ls_remote () { __gitcomp "$(__git_remotes)" @@ -1614,6 +1634,7 @@ _git () help) _git_help ;; init) _git_init ;; log) _git_log ;; + ls-files) _git_ls_files ;; ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; merge) _git_merge;; -- cgit v1.2.3 From 1127c51cfe09f0d7d26869e7a895190214ce049a Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 4 Aug 2008 23:50:38 -0600 Subject: bash completion: Add completion for 'git mv' Add completions for all long options specified in the docs --dry-run Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7a7bc99955..3396e35d76 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1006,6 +1006,18 @@ _git_merge_base () __gitcomp "$(__git_refs)" } +_git_mv () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--dry-run" + return + ;; + esac + COMPREPLY=() +} + _git_name_rev () { __gitcomp "--tags --all --stdin" @@ -1639,6 +1651,7 @@ _git () ls-tree) _git_ls_tree ;; merge) _git_merge;; merge-base) _git_merge_base ;; + mv) _git_mv ;; name-rev) _git_name_rev ;; pull) _git_pull ;; push) _git_push ;; -- cgit v1.2.3 From 5a7ebd4faab1430a6d4daef0636345709139e3b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Wed, 6 Aug 2008 17:45:23 +0200 Subject: bash: remove redundant check for 'git stash apply' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will never trigger anyway because of the first check, and even if it would, it would not offer the command line option. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3396e35d76..78189c1b7b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1439,7 +1439,7 @@ _git_stash () apply,--*) __gitcomp "--index" ;; - show,--*|apply,--*|drop,--*|pop,--*|branch,--*) + show,--*|drop,--*|pop,--*|branch,--*) COMPREPLY=() ;; show,*|apply,*|drop,*|pop,*|branch,*) -- cgit v1.2.3 From 2318121babea786390f51796bfce17088c3420ee Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:24 +0100 Subject: Create a specific version of the read_pipe_lines command for p4 invocations This will make it easier to isolate changes to how 'p4' is invoked (whether with parameters or not, etc.). Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6ae0429c2d..fc2a60dfee 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -57,6 +57,13 @@ def read_pipe_lines(c): return val +def p4_read_pipe_lines(c): + """Specifically invoke p4 on the command supplied. """ + real_cmd = "%s %s" % ("p4", c) + if verbose: + print real_cmd + return read_pipe_lines(real_cmd) + def system(cmd): if verbose: sys.stderr.write("executing %s\n" % cmd) -- cgit v1.2.3 From b340fa43017437988e233ed4fd8dc00042614071 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:25 +0100 Subject: Utilise the new 'p4_read_pipe_lines' command Now that we have the new command, we can utilise it and then eventually, isolate any changes required to the one place. Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index fc2a60dfee..3deaa42559 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -371,7 +371,7 @@ def originP4BranchesExist(): def p4ChangesForPaths(depotPaths, changeRange): assert depotPaths - output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, changeRange) + output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange) for p in depotPaths])) changes = [] @@ -519,7 +519,7 @@ class P4Submit(Command): # remove lines in the Files section that show changes to files outside the depot path we're committing into template = "" inFilesSection = False - for line in read_pipe_lines("p4 change -o"): + for line in p4_read_pipe_lines("change -o"): if line.endswith("\r\n"): line = line[:-2] + "\n" if inFilesSection: -- cgit v1.2.3 From bf9320f1512d7ad4a17a64cfe5a593bba5037b3e Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:26 +0100 Subject: Have a command that specifically invokes 'p4' (via system) Similiar to our 'p4_read_pipe_lines' command, we can isolate specific changes to the invocation method in the one location with this change. Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3deaa42559..08acd517ba 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -70,6 +70,13 @@ def system(cmd): if os.system(cmd) != 0: die("command failed: %s" % cmd) +def p4_system(cmd): + """Specifically invoke p4 as the system command. """ + real_cmd = "%s %s" % ("p4", cmd) + if verbose: + print real_cmd + return system(real_cmd) + def isP4Exec(kind): """Determine if a Perforce 'kind' should have execute permission -- cgit v1.2.3 From 87b611d5fd518e4754e599bc2f348f83db410c56 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:27 +0100 Subject: Utilise the new 'p4_system' function. Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 08acd517ba..2ed36ecd6b 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -98,7 +98,7 @@ def setP4ExecBit(file, mode): if p4Type[-1] == "+": p4Type = p4Type[0:-1] - system("p4 reopen -t %s %s" % (p4Type, file)) + p4_system("reopen -t %s %s" % (p4Type, file)) def getP4OpenedType(file): # Returns the perforce file type for the given file. @@ -561,7 +561,7 @@ class P4Submit(Command): modifier = diff['status'] path = diff['src'] if modifier == "M": - system("p4 edit \"%s\"" % path) + p4_system("edit \"%s\"" % path) if isModeExecChanged(diff['src_mode'], diff['dst_mode']): filesToChangeExecBit[path] = diff['dst_mode'] editedFiles.add(path) @@ -576,8 +576,8 @@ class P4Submit(Command): filesToAdd.remove(path) elif modifier == "R": src, dest = diff['src'], diff['dst'] - system("p4 integrate -Dt \"%s\" \"%s\"" % (src, dest)) - system("p4 edit \"%s\"" % (dest)) + p4_system("integrate -Dt \"%s\" \"%s\"" % (src, dest)) + p4_system("edit \"%s\"" % (dest)) if isModeExecChanged(diff['src_mode'], diff['dst_mode']): filesToChangeExecBit[dest] = diff['dst_mode'] os.unlink(dest) @@ -601,7 +601,7 @@ class P4Submit(Command): if response == "s": print "Skipping! Good luck with the next patches..." for f in editedFiles: - system("p4 revert \"%s\"" % f); + p4_system("revert \"%s\"" % f); for f in filesToAdd: system("rm %s" %f) return @@ -624,10 +624,10 @@ class P4Submit(Command): system(applyPatchCmd) for f in filesToAdd: - system("p4 add \"%s\"" % f) + p4_system("add \"%s\"" % f) for f in filesToDelete: - system("p4 revert \"%s\"" % f) - system("p4 delete \"%s\"" % f) + p4_system("revert \"%s\"" % f) + p4_system("delete \"%s\"" % f) # Set/clear executable bits for f in filesToChangeExecBit.keys(): @@ -728,7 +728,7 @@ class P4Submit(Command): os.chdir(self.clientPath) print "Syncronizing p4 checkout..." - system("p4 sync ...") + p4_system("sync ...") self.check() -- cgit v1.2.3 From 21a50753852cb51b120ec9933416daa6cea6d184 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:28 +0100 Subject: Add a single command that will be used to construct the 'p4' command Rather than having three locations where the 'p4' command is built up, refactor this into the one place. This will, eventually, allow us to have one place where we modify the evironment or pass extra command-line options to the 'p4' binary. Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2ed36ecd6b..b4acf7689d 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -16,6 +16,17 @@ from sets import Set; verbose = False + +def p4_build_cmd(cmd): + """Build a suitable p4 command line. + + This consolidates building and returning a p4 command line into one + location. It means that hooking into the environment, or other configuration + can be done more easily. + """ + real_cmd = "%s %s" % ("p4", cmd) + return real_cmd + def die(msg): if verbose: raise Exception(msg) -- cgit v1.2.3 From ee06427aa6d975c35b63c3cd103ace43fbde062b Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:29 +0100 Subject: If we are in verbose mode, output what we are about to run (or return) Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b4acf7689d..d36b0c6bec 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -25,6 +25,8 @@ def p4_build_cmd(cmd): can be done more easily. """ real_cmd = "%s %s" % ("p4", cmd) + if verbose: + print real_cmd return real_cmd def die(msg): -- cgit v1.2.3 From 155af83491b26d958b147c93620816846343b019 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:30 +0100 Subject: Switch to using 'p4_build_cmd' Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index d36b0c6bec..2b6ea74d1c 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -72,9 +72,7 @@ def read_pipe_lines(c): def p4_read_pipe_lines(c): """Specifically invoke p4 on the command supplied. """ - real_cmd = "%s %s" % ("p4", c) - if verbose: - print real_cmd + real_cmd = p4_build_cmd(c) return read_pipe_lines(real_cmd) def system(cmd): @@ -85,9 +83,7 @@ def system(cmd): def p4_system(cmd): """Specifically invoke p4 as the system command. """ - real_cmd = "%s %s" % ("p4", cmd) - if verbose: - print real_cmd + real_cmd = p4_build_cmd(cmd) return system(real_cmd) def isP4Exec(kind): @@ -172,7 +168,7 @@ def isModeExecChanged(src_mode, dst_mode): return isModeExec(src_mode) != isModeExec(dst_mode) def p4CmdList(cmd, stdin=None, stdin_mode='w+b'): - cmd = "p4 -G %s" % cmd + cmd = p4_build_cmd("-G %s" % (cmd)) if verbose: sys.stderr.write("Opening pipe: %s\n" % cmd) -- cgit v1.2.3 From abcaf07360357cf2e9ce4b34e44adc09bb5587f0 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:31 +0100 Subject: If the user has configured various parameters, use them. Some repositories require authentication and access to certain hosts. Allow git-p4 to pull this information from the configuration Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2b6ea74d1c..a927e50b25 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -24,7 +24,29 @@ def p4_build_cmd(cmd): location. It means that hooking into the environment, or other configuration can be done more easily. """ - real_cmd = "%s %s" % ("p4", cmd) + real_cmd = "%s " % "p4" + + user = gitConfig("git-p4.user") + if len(user) > 0: + real_cmd += "-u %s " % user + + password = gitConfig("git-p4.password") + if len(password) > 0: + real_cmd += "-P %s " % password + + port = gitConfig("git-p4.port") + if len(port) > 0: + real_cmd += "-p %s " % port + + host = gitConfig("git-p4.host") + if len(host) > 0: + real_cmd += "-h %s " % host + + client = gitConfig("git-p4.client") + if len(client) > 0: + real_cmd += "-c %s " % client + + real_cmd += "%s" % (cmd) if verbose: print real_cmd return real_cmd -- cgit v1.2.3 From 3cafb7d8ce63effe6bf477182d431ecb4470eded Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:32 +0100 Subject: Consistently use 'git-p4' for the configuration entries Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a927e50b25..6c64224b77 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1444,7 +1444,7 @@ class P4Sync(Command): if not gitBranchExists(self.refPrefix + "HEAD") and self.importIntoRemotes and gitBranchExists(self.branch): system("git symbolic-ref %sHEAD %s" % (self.refPrefix, self.branch)) - if self.useClientSpec or gitConfig("p4.useclientspec") == "true": + if self.useClientSpec or gitConfig("git-p4.useclientspec") == "true": self.getClientSpec() # TODO: should always look at previous commits, -- cgit v1.2.3 From bc02acfc769a1ae19772feaa7f03acfaea18a36f Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:33 +0100 Subject: Move git-p4.syncFromOrigin into a configuration parameters section Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4.txt | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index b16a8384bc..0896abb933 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -63,18 +63,6 @@ It is recommended to run 'git repack -a -d -f' from time to time when using incremental imports to optimally combine the individual git packs that each incremental import creates through the use of git-fast-import. - -A useful setup may be that you have a periodically updated git repository -somewhere that contains a complete import of a Perforce project. That git -repository can be used to clone the working repository from and one would -import from Perforce directly after cloning using git-p4. If the connection to -the Perforce server is slow and the working repository hasn't been synced for a -while it may be desirable to fetch changes from the origin git repository using -the efficient git protocol. git-p4 supports this setup by calling "git fetch origin" -by default if there is an origin branch. You can disable this using - - git config git-p4.syncFromOrigin false - Updating ======== @@ -140,6 +128,22 @@ Example git-p4 rebase +Configuration parameters +======================== + +git-p4.syncFromOrigin + +A useful setup may be that you have a periodically updated git repository +somewhere that contains a complete import of a Perforce project. That git +repository can be used to clone the working repository from and one would +import from Perforce directly after cloning using git-p4. If the connection to +the Perforce server is slow and the working repository hasn't been synced for a +while it may be desirable to fetch changes from the origin git repository using +the efficient git protocol. git-p4 supports this setup by calling "git fetch origin" +by default if there is an origin branch. You can disable this using: + + git config [--global] git-p4.syncFromOrigin false + Implementation Details... ========================= -- cgit v1.2.3 From b87a659635f40b5301c6b18fa5e22c72ca79b830 Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:34 +0100 Subject: Put some documentation in about the parameters that have been added Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4.txt | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 0896abb933..79a22e9c10 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -131,6 +131,38 @@ Example Configuration parameters ======================== +git-p4.user ($P4USER) + +Allows you to specify the username to use to connect to the Perforce repository. + + git config [--global] git-p4.user public + +git-p4.password ($P4PASS) + +Allows you to specify the password to use to connect to the Perforce repository. +Warning this password will be visible on the command-line invocation of the p4 binary. + + git config [--global] git-p4.password public1234 + +git-p4.port ($P4PORT) + +Specify the port to be used to contact the Perforce server. As this will be passed +directly to the p4 binary, it may be in the format host:port as well. + + git config [--global] git-p4.port codes.zimbra.com:2666 + +git-p4.host ($P4HOST) + +Specify the host to contact for a Perforce repository. + + git config [--global] git-p4.host perforce.example.com + +git-p4.client ($P4CLIENT) + +Specify the client name to use + + git config [--global] git-p4.client public-view + git-p4.syncFromOrigin A useful setup may be that you have a periodically updated git repository -- cgit v1.2.3 From 5b5aa22f00c315021ff58450f18134b20dfd5abd Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Sun, 10 Aug 2008 19:26:35 +0100 Subject: Put in the two other configuration elements found in the source I am not entirely clear what these parameters do but felt it useful to call them out in the documentation. Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4.txt | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index 79a22e9c10..ac551d45f1 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -163,6 +163,10 @@ Specify the client name to use git config [--global] git-p4.client public-view +git-p4.allowSubmit + + git config [--global] git-p4.allowSubmit false + git-p4.syncFromOrigin A useful setup may be that you have a periodically updated git repository @@ -176,6 +180,10 @@ by default if there is an origin branch. You can disable this using: git config [--global] git-p4.syncFromOrigin false +git-p4.useclientspec + + git config [--global] git-p4.useclientspec false + Implementation Details... ========================= -- cgit v1.2.3 From 7950659dc9ef7f2b50b18010622299c508bfdfc3 Mon Sep 17 00:00:00 2001 From: Eric Raible Date: Thu, 14 Aug 2008 10:12:54 -0700 Subject: bash completion: 'git apply' should use 'fix' not 'strip' Bring completion up to date with the man page. Signed-off-by: Eric Raible Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 78189c1b7b..d1afe96e1c 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -501,7 +501,7 @@ __git_has_doubledash () return 1 } -__git_whitespacelist="nowarn warn error error-all strip" +__git_whitespacelist="nowarn warn error error-all fix" _git_am () { -- cgit v1.2.3 From b4c72162f6612ce335af79ef19c5ae16f5585e67 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Thu, 14 Aug 2008 16:41:10 -0600 Subject: bash completion: Add completion for 'git mergetool' The --tool= long option to "git mergetool" can be completed with: kdiff3 tkdiff meld xxdiff emerge vimdiff gvimdiff ecmerge opendiff Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d1afe96e1c..2f8036d1d9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1001,6 +1001,25 @@ _git_merge () __gitcomp "$(__git_refs)" } +_git_mergetool () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --tool=*) + __gitcomp " + kdiff3 tkdiff meld xxdiff emerge + vimdiff gvimdiff ecmerge opendiff + " "" "${cur##--tool=}" + return + ;; + --*) + __gitcomp "--tool=" + return + ;; + esac + COMPREPLY=() +} + _git_merge_base () { __gitcomp "$(__git_refs)" @@ -1650,6 +1669,7 @@ _git () ls-remote) _git_ls_remote ;; ls-tree) _git_ls_tree ;; merge) _git_merge;; + mergetool) _git_mergetool;; merge-base) _git_merge_base ;; mv) _git_mv ;; name-rev) _git_name_rev ;; -- cgit v1.2.3 From 5a13c8f6f7ef7463ddaa3dd7144bf66af4fcd9be Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Thu, 14 Aug 2008 16:41:11 -0600 Subject: bash completion: Add '--merge' long option for 'git log' Signed-off-by: Lee Marlow Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2f8036d1d9..c0bf7aade6 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -972,6 +972,7 @@ _git_log () --decorate --diff-filter= --color-words --walk-reflogs --parents --children --full-history + --merge " return ;; -- cgit v1.2.3 From d9429194f6e30e1f6f46a286217cd88972e1c83b Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Thu, 14 Aug 2008 23:40:38 +0100 Subject: Add p4 read_pipe and write_pipe wrappers Two additional wrappers to cover 3 places where we utilise p4 in piped form. Found by Tor Arvid Lund. Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6c64224b77..3e9df70f29 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -69,6 +69,10 @@ def write_pipe(c, str): return val +def p4_write_pipe(c, str): + real_cmd = p4_build_cmd(c) + return write_pipe(c, str) + def read_pipe(c, ignore_error=False): if verbose: sys.stderr.write('Reading pipe: %s\n' % c) @@ -80,6 +84,9 @@ def read_pipe(c, ignore_error=False): return val +def p4_read_pipe(c, ignore_error=False): + real_cmd = p4_build_cmd(c) + return read_pipe(real_cmd, ignore_error) def read_pipe_lines(c): if verbose: -- cgit v1.2.3 From a7d3ef9d099ab00a19595bc3ca8abdc1fc6ff35d Mon Sep 17 00:00:00 2001 From: Anand Kumria Date: Thu, 14 Aug 2008 23:40:39 +0100 Subject: Utilise our new p4_read_pipe and p4_write_pipe wrappers Signed-off-by: Anand Kumria Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3e9df70f29..12fa9d3fd8 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -141,7 +141,7 @@ def setP4ExecBit(file, mode): def getP4OpenedType(file): # Returns the perforce file type for the given file. - result = read_pipe("p4 opened %s" % file) + result = p4_read_pipe("opened %s" % file) match = re.match(".*\((.+)\)\r?$", result) if match: return match.group(1) @@ -681,7 +681,7 @@ class P4Submit(Command): submitTemplate = self.prepareLogMessage(template, logMessage) if os.environ.has_key("P4DIFF"): del(os.environ["P4DIFF"]) - diff = read_pipe("p4 diff -du ...") + diff = p4_read_pipe("diff -du ...") newdiff = "" for newFile in filesToAdd: @@ -719,7 +719,7 @@ class P4Submit(Command): if self.isWindows: submitTemplate = submitTemplate.replace("\r\n", "\n") - write_pipe("p4 submit -i", submitTemplate) + p4_write_pipe("submit -i", submitTemplate) else: fileName = "submit.txt" file = open(fileName, "w+") -- cgit v1.2.3 From 2946cccfdf2fba591b6af61ad6e658bb927832af Mon Sep 17 00:00:00 2001 From: Marcus Griep Date: Fri, 15 Aug 2008 13:59:28 -0400 Subject: bash-completion: Add non-command git help files to bash-completion Git allows access to the gitattributes man page via `git help attributes`, but this is not discoverable via the bash-completion mechanism. This patch adds all current non-command man pages to the completion candidate list. Signed-off-by: Marcus Griep Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c0bf7aade6..158b912841 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -885,7 +885,11 @@ _git_help () return ;; esac - __gitcomp "$(__git_all_commands)" + __gitcomp "$(__git_all_commands) + attributes cli core-tutorial cvs-migration + diffcore gitk glossary hooks ignore modules + repository-layout tutorial tutorial-2 + " } _git_init () -- cgit v1.2.3 From 053fd0c1c3da20474c4ff175c56ea4c1d6eeda11 Mon Sep 17 00:00:00 2001 From: Robert Blum Date: Fri, 1 Aug 2008 12:50:03 -0700 Subject: git-p4: chdir now properly sets PWD environment variable in msysGit P4 on Windows expects the PWD environment variable to be set to the current working dir, but os.chdir in python doesn't do so. Signed-off-by: Robert Blum Acked-by: Simon Hausmann Acked-by: Han-Wen Nienhuys Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 6ae0429c2d..3f2303dcf0 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -16,6 +16,11 @@ from sets import Set; verbose = False +def chdir(dir): + if os.name == 'nt': + os.environ['PWD']=dir + os.chdir(dir) + def die(msg): if verbose: raise Exception(msg) @@ -712,7 +717,7 @@ class P4Submit(Command): print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath) self.oldWorkingDirectory = os.getcwd() - os.chdir(self.clientPath) + chdir(self.clientPath) print "Syncronizing p4 checkout..." system("p4 sync ...") @@ -732,7 +737,7 @@ class P4Submit(Command): if len(commits) == 0: print "All changes applied!" - os.chdir(self.oldWorkingDirectory) + chdir(self.oldWorkingDirectory) sync = P4Sync() sync.run([]) @@ -1670,7 +1675,7 @@ class P4Clone(P4Sync): print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination) if not os.path.exists(self.cloneDestination): os.makedirs(self.cloneDestination) - os.chdir(self.cloneDestination) + chdir(self.cloneDestination) system("git init") self.gitdir = os.getcwd() + "/.git" if not P4Sync.run(self, depotPaths): @@ -1782,7 +1787,7 @@ def main(): if os.path.exists(cmd.gitdir): cdup = read_pipe("git rev-parse --show-cdup").strip() if len(cdup) > 0: - os.chdir(cdup); + chdir(cdup); if not isValidGitDir(cmd.gitdir): if isValidGitDir(cmd.gitdir + "/.git"): -- cgit v1.2.3 From 25b3d4d6f39d70c4d46dee48570ae7aeeb4a6b58 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 20 Aug 2008 14:13:42 -0700 Subject: completion: find out supported merge strategies correctly "git-merge" is a binary executable these days, and looking for assignment to $all_strategies variable with grep/sed does not work well. When asked for an unknown strategy, pre-1.6.0 and post-1.6.0 "git merge" commands respectively say: $ $HOME/git-snap-v1.5.6.5/bin/git merge -s help available strategies are: recur recursive octopus resolve stupid ours subtree $ $HOME/git-snap-v1.6.0/bin/git merge -s help Could not find merge strategy 'help'. Available strategies are: recursive octopus resolve ours subtree. both on their standard error stream. We can use this to learn what strategies are supported. The sed script is written in such a way that it catches both old and new message styles ("Available" vs "available", and the full stop at the end). It also allows future versions of "git merge" to line-wrap the list of strategies, and add extra comments, like this: $ $HOME/git-snap-v1.6.1/bin/git merge -s help Could not find merge strategy 'help'. Available strategies are: blame recursive octopus resolve ours subtree. Also you have custom strategies: theirs Make sure you spell strategy names correctly. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 158b912841..a31004088a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -271,15 +271,17 @@ __git_merge_strategies () echo "$__git_merge_strategylist" return fi - sed -n "/^all_strategies='/{ - s/^all_strategies='// - s/'// + git merge -s help 2>&1 | + sed -n -e '/[Aa]vailable strategies are: /,/^$/{ + s/\.$// + s/.*:// + s/^[ ]*// + s/[ ]*$// p - q - }" "$(git --exec-path)/git-merge" + }' } __git_merge_strategylist= -__git_merge_strategylist="$(__git_merge_strategies 2>/dev/null)" +__git_merge_strategylist=$(__git_merge_strategies 2>/dev/null) __git_complete_file () { -- cgit v1.2.3 From f135aacb5ae30b54bac0dde7462b532d19e4c0d6 Mon Sep 17 00:00:00 2001 From: Eric Raible Date: Fri, 22 Aug 2008 10:25:06 -0700 Subject: Completion: add missing '=' for 'diff --diff-filter' Signed-off-by: Eric Raible Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a31004088a..89858c237e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -771,7 +771,7 @@ _git_diff () __gitcomp "--cached --stat --numstat --shortstat --summary --patch-with-stat --name-only --name-status --color --no-color --color-words --no-renames --check - --full-index --binary --abbrev --diff-filter + --full-index --binary --abbrev --diff-filter= --find-copies-harder --pickaxe-all --pickaxe-regex --text --ignore-space-at-eol --ignore-space-change --ignore-all-space --exit-code --quiet --ext-diff -- cgit v1.2.3 From 893d340f2c735dc85b9360556ccd46cc0bf27fc0 Mon Sep 17 00:00:00 2001 From: Tor Arvid Lund Date: Thu, 21 Aug 2008 23:11:40 +0200 Subject: git-p4: Fix one-liner in p4_write_pipe function. The function built a p4 command string via the p4_build_cmd function, but ignored the result. Signed-off-by: Tor Arvid Lund Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index f9865b444f..46136d49bf 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -76,7 +76,7 @@ def write_pipe(c, str): def p4_write_pipe(c, str): real_cmd = p4_build_cmd(c) - return write_pipe(c, str) + return write_pipe(real_cmd, str) def read_pipe(c, ignore_error=False): if verbose: -- cgit v1.2.3 From f5f7e4a18cf656747be8f8447ca304ddf716c742 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sun, 24 Aug 2008 16:12:23 +0200 Subject: Clean up the git-p4 documentation This patch massages the documentation a bit for improved readability and cleans it up from outdated options/commands. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4.txt | 69 +++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 31 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4.txt b/contrib/fast-import/git-p4.txt index ac551d45f1..49b335921a 100644 --- a/contrib/fast-import/git-p4.txt +++ b/contrib/fast-import/git-p4.txt @@ -3,14 +3,16 @@ git-p4 - Perforce <-> Git converter using git-fast-import Usage ===== -git-p4 supports two main modes: Importing from Perforce to a Git repository is -done using "git-p4 sync" or "git-p4 rebase". Submitting changes from Git back -to Perforce is done using "git-p4 submit". +git-p4 can be used in two different ways: + +1) To import changes from Perforce to a Git repository, using "git-p4 sync". + +2) To submit changes from Git back to Perforce, using "git-p4 submit". Importing ========= -You can simply start with +Simply start with git-p4 clone //depot/path/project @@ -18,11 +20,18 @@ or git-p4 clone //depot/path/project myproject -This will create an empty git repository in a subdirectory called "project" (or -"myproject" with the second command), import the head revision from the -specified perforce path into a git "p4" branch (remotes/p4 actually), create a -master branch off it and check it out. If you want the entire history (not just -the head revision) then you can simply append a "@all" to the depot path: +This will: + +1) Create an empty git repository in a subdirectory called "project" (or +"myproject" with the second command) + +2) Import the head revision from the given Perforce path into a git branch +called "p4" (remotes/p4 actually) + +3) Create a master branch based on it and check it out. + +If you want the entire history (not just the head revision) then you can simply +append a "@all" to the depot path: git-p4 clone //depot/project/main@all myproject @@ -37,31 +46,40 @@ If you want more control you can also use the git-p4 sync command directly: This will import the current head revision of the specified depot path into a "remotes/p4/master" branch of your git repository. You can use the ---branch=mybranch option to use a different branch. +--branch=mybranch option to import into a different branch. -If you want to import the entire history of a given depot path just use +If you want to import the entire history of a given depot path simply use: git-p4 sync //path/in/depot@all + +Note: + To achieve optimal compression you may want to run 'git repack -a -d -f' after a big import. This may take a while. -Support for Perforce integrations is still work in progress. Don't bother -trying it unless you want to hack on it :) - Incremental Imports =================== -After an initial import you can easily synchronize your git repository with -newer changes from the Perforce depot by just calling +After an initial import you can continue to synchronize your git repository +with newer changes from the Perforce depot by just calling git-p4 sync in your git repository. By default the "remotes/p4/master" branch is updated. -It is recommended to run 'git repack -a -d -f' from time to time when using -incremental imports to optimally combine the individual git packs that each -incremental import creates through the use of git-fast-import. +Advanced Setup +============== + +Suppose you have a periodically updated git repository somewhere, containing a +complete import of a Perforce project. This repository can be cloned and used +with git-p4. When updating the cloned repository with the "sync" command, +git-p4 will try to fetch changes from the original repository first. The git +protocol used with this is usually faster than importing from Perforce +directly. + +This behaviour can be disabled by setting the "git-p4.syncFromOrigin" git +configuration variable to "false". Updating ======== @@ -79,7 +97,7 @@ Submitting ========== git-p4 has support for submitting changes from a git repository back to the -Perforce depot. This requires a Perforce checkout separate to your git +Perforce depot. This requires a Perforce checkout separate from your git repository. To submit all changes that are in the current git branch but not in the "p4" branch (or "origin" if "p4" doesn't exist) simply call @@ -97,17 +115,6 @@ continue importing the remaining changes with git-p4 submit --continue -After submitting you should sync your perforce import branch ("p4" or "origin") -from Perforce using git-p4's sync command. - -If you have changes in your working directory that you haven't committed into -git yet but that you want to commit to Perforce directly ("quick fixes") then -you do not have to go through the intermediate step of creating a git commit -first but you can just call - - git-p4 submit --direct - - Example ======= -- cgit v1.2.3 From cdc7e388da47b66fe11c92ed7698ec233ce70635 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 27 Aug 2008 09:30:29 +0200 Subject: Make it possible to abort the submission of a change to Perforce Currently it is not possible to skip the submission of a change to Perforce when running git-p4 submit. This patch compares the modification time before and after the submit editor invokation and offers a prompt for skipping if the submit template file was not saved. Signed-off-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 46136d49bf..c1d24b38f3 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -708,6 +708,7 @@ class P4Submit(Command): newdiff = newdiff.replace("\n", "\r\n") tmpFile.write(submitTemplate + separatorLine + diff + newdiff) tmpFile.close() + mtime = os.stat(fileName).st_mtime defaultEditor = "vi" if platform.system() == "Windows": defaultEditor = "notepad" @@ -716,15 +717,29 @@ class P4Submit(Command): else: editor = os.environ.get("EDITOR", defaultEditor); system(editor + " " + fileName) - tmpFile = open(fileName, "rb") - message = tmpFile.read() - tmpFile.close() - os.remove(fileName) - submitTemplate = message[:message.index(separatorLine)] - if self.isWindows: - submitTemplate = submitTemplate.replace("\r\n", "\n") - p4_write_pipe("submit -i", submitTemplate) + response = "y" + if os.stat(fileName).st_mtime <= mtime: + response = "x" + while response != "y" and response != "n": + response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ") + + if response == "y": + tmpFile = open(fileName, "rb") + message = tmpFile.read() + tmpFile.close() + submitTemplate = message[:message.index(separatorLine)] + if self.isWindows: + submitTemplate = submitTemplate.replace("\r\n", "\n") + p4_write_pipe("submit -i", submitTemplate) + else: + for f in editedFiles: + p4_system("revert \"%s\"" % f); + for f in filesToAdd: + p4_system("revert \"%s\"" % f); + system("rm %s" %f) + + os.remove(fileName) else: fileName = "submit.txt" file = open(fileName, "w+") -- cgit v1.2.3 From 1b0f7978ddb9e2ed4437ce68a4b82ab831288a41 Mon Sep 17 00:00:00 2001 From: Matthias Kestenholz Date: Thu, 28 Aug 2008 10:57:55 +0200 Subject: bash-completion: Add all submodule subcommands to the completion list Signed-off-by: Matthias Kestenholz Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 89858c237e..4f64f8ab7d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1483,7 +1483,7 @@ _git_submodule () { __git_has_doubledash && return - local subcommands="add status init update" + local subcommands="add status init update summary foreach sync" if [ -z "$(__git_find_subcommand "$subcommands")" ]; then local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in -- cgit v1.2.3 From e990501312e22cfa910d88dc7143bc4eb3632ae1 Mon Sep 17 00:00:00 2001 From: Tor Arvid Lund Date: Thu, 28 Aug 2008 00:36:12 +0200 Subject: git-p4: Fix checkout bug when using --import-local. When this option is passed to git p4 clone, the checkout at the end would previously fail. This patch fixes it by optionally creating the master branch from refs/heads/p4/master, which is the correct one for this option. Signed-off-by: Tor Arvid Lund Acked-By: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index c1d24b38f3..2216cacba7 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1748,8 +1748,12 @@ class P4Clone(P4Sync): if not P4Sync.run(self, depotPaths): return False if self.branch != "master": - if gitBranchExists("refs/remotes/p4/master"): - system("git branch master refs/remotes/p4/master") + if self.importIntoRemotes: + masterbranch = "refs/remotes/p4/master" + else: + masterbranch = "refs/heads/p4/master" + if gitBranchExists(masterbranch): + system("git branch master %s" % masterbranch) system("git checkout -f") else: print "Could not detect main branch. No checkout/master branch created." -- cgit v1.2.3 From ff2549dc9af3fffa8a8285418601d9eab94de7b7 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 26 Aug 2008 19:11:44 +0200 Subject: bash completion: Hide more plumbing commands git still shows way too many commands, some of them are clearly plumbing. This patch hides the plumbing commands liberally (that is, in special cases, users still might want to call one of the hidden commands, a *normal* workflow should never involve these, though - and if it does, we have a UI problem anyway). Signed-off-by: Petr Baudis Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 89858c237e..1154ae1ac8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -386,7 +386,9 @@ __git_porcelain_commands () cat-file) : plumbing;; check-attr) : plumbing;; check-ref-format) : plumbing;; + checkout-index) : plumbing;; commit-tree) : plumbing;; + count-objects) : infrequent;; cvsexportcommit) : export;; cvsimport) : import;; cvsserver) : daemon;; @@ -395,6 +397,7 @@ __git_porcelain_commands () diff-index) : plumbing;; diff-tree) : plumbing;; fast-import) : import;; + fast-export) : export;; fsck-objects) : plumbing;; fetch-pack) : plumbing;; fmt-merge-msg) : plumbing;; @@ -404,6 +407,10 @@ __git_porcelain_commands () index-pack) : plumbing;; init-db) : deprecated;; local-fetch) : plumbing;; + lost-found) : infrequent;; + ls-files) : plumbing;; + ls-remote) : plumbing;; + ls-tree) : plumbing;; mailinfo) : plumbing;; mailsplit) : plumbing;; merge-*) : plumbing;; @@ -428,6 +435,7 @@ __git_porcelain_commands () runstatus) : plumbing;; sh-setup) : internal;; shell) : daemon;; + show-ref) : plumbing;; send-pack) : plumbing;; show-index) : plumbing;; ssh-*) : transport;; @@ -442,6 +450,8 @@ __git_porcelain_commands () upload-archive) : plumbing;; upload-pack) : plumbing;; write-tree) : plumbing;; + var) : infrequent;; + verify-pack) : infrequent;; verify-tag) : plumbing;; *) echo $i;; esac -- cgit v1.2.3 From aa5735bed459be71473b4fda1a9a026f798acf76 Mon Sep 17 00:00:00 2001 From: Eric Raible Date: Wed, 10 Sep 2008 17:40:20 -0700 Subject: completion: git commit should list --interactive Signed-off-by: Eric Raible Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1154ae1ac8..3bc45f6b47 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -750,7 +750,7 @@ _git_commit () --*) __gitcomp " --all --author= --signoff --verify --no-verify - --edit --amend --include --only + --edit --amend --include --only --interactive " return esac -- cgit v1.2.3 From 05e8b3d6f43bd28aef85ec6e7658536510d6f959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sat, 20 Sep 2008 00:15:25 +0200 Subject: bash: use for-each-ref format 'refname:short' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using this format simplifies the code for completing refs and (in some cases) improves performance significantly. For repositories like the current git.git (with more than 200 refs) there is no real performance difference, but for a repository with 2000 refs the total time needed to complete the refs is reduced by ~25% (from around 400ms to around 305ms). Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d3fb6ae507..fccb499850 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -154,11 +154,8 @@ __git_heads () { local cmd i is_hash=y dir="$(__gitdir "$1")" if [ -d "$dir" ]; then - for i in $(git --git-dir="$dir" \ - for-each-ref --format='%(refname)' \ - refs/heads ); do - echo "${i#refs/heads/}" - done + git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ + refs/heads return fi for i in $(git ls-remote "$1" 2>/dev/null); do @@ -175,11 +172,8 @@ __git_tags () { local cmd i is_hash=y dir="$(__gitdir "$1")" if [ -d "$dir" ]; then - for i in $(git --git-dir="$dir" \ - for-each-ref --format='%(refname)' \ - refs/tags ); do - echo "${i#refs/tags/}" - done + git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ + refs/tags return fi for i in $(git ls-remote "$1" 2>/dev/null); do @@ -197,16 +191,8 @@ __git_refs () local cmd i is_hash=y dir="$(__gitdir "$1")" if [ -d "$dir" ]; then if [ -e "$dir/HEAD" ]; then echo HEAD; fi - for i in $(git --git-dir="$dir" \ - for-each-ref --format='%(refname)' \ - refs/tags refs/heads refs/remotes); do - case "$i" in - refs/tags/*) echo "${i#refs/tags/}" ;; - refs/heads/*) echo "${i#refs/heads/}" ;; - refs/remotes/*) echo "${i#refs/remotes/}" ;; - *) echo "$i" ;; - esac - done + git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ + refs/tags refs/heads refs/remotes return fi for i in $(git ls-remote "$dir" 2>/dev/null); do -- cgit v1.2.3 From 18309f4c3e00886660f15e18c1aaab2f5bc25715 Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Mon, 22 Sep 2008 07:30:08 -0400 Subject: Use dashless git commands in setgitperms.perl Signed-off-by: Todd Zullinger Signed-off-by: Junio C Hamano --- contrib/hooks/setgitperms.perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/setgitperms.perl b/contrib/hooks/setgitperms.perl index dab7c8e3a1..a577ad095f 100644 --- a/contrib/hooks/setgitperms.perl +++ b/contrib/hooks/setgitperms.perl @@ -50,7 +50,7 @@ if ((@ARGV < 0) || !GetOptions( )) { die $usage; } die $usage unless ($read_mode xor $write_mode); -my $topdir = `git-rev-parse --show-cdup` or die "\n"; chomp $topdir; +my $topdir = `git rev-parse --show-cdup` or die "\n"; chomp $topdir; my $gitdir = $topdir . '.git'; my $gitmeta = $topdir . '.gitmeta'; @@ -155,7 +155,7 @@ elsif ($read_mode) { open (OUT, ">$gitmeta.tmp") or die "Could not open $gitmeta.tmp for writing: $!\n"; } - my @files = `git-ls-files`; + my @files = `git ls-files`; my %dirs; foreach my $path (@files) { -- cgit v1.2.3 From c11c7a5db3676258512c4067c5279377811d3ab8 Mon Sep 17 00:00:00 2001 From: Nanako Shiraishi Date: Sat, 27 Sep 2008 20:44:15 +0900 Subject: Add contrib/rerere-train script This script takes a range of commits (e.g. maint..next) as its arguments, recreates merge commits in the range to prime rr-cache database. Signed-off-by: Nanako Shiraishi Signed-off-by: Shawn O. Pearce --- contrib/rerere-train.sh | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100755 contrib/rerere-train.sh (limited to 'contrib') diff --git a/contrib/rerere-train.sh b/contrib/rerere-train.sh new file mode 100755 index 0000000000..2cfe1b936b --- /dev/null +++ b/contrib/rerere-train.sh @@ -0,0 +1,52 @@ +#!/bin/sh +# Copyright (c) 2008, Nanako Shiraishi +# Prime rerere database from existing merge commits + +me=rerere-train +USAGE="$me rev-list-args" + +SUBDIRECTORY_OK=Yes +OPTIONS_SPEC= +. git-sh-setup +require_work_tree +cd_to_toplevel + +# Remember original branch +branch=$(git symbolic-ref -q HEAD) || +original_HEAD=$(git rev-parse --verify HEAD) || { + echo >&2 "Not on any branch and no commit yet?" + exit 1 +} + +mkdir -p "$GIT_DIR/rr-cache" || exit + +git rev-list --parents "$@" | +while read commit parent1 other_parents +do + if test -z "$other_parents" + then + # Skip non-merges + continue + fi + git checkout -q "$parent1^0" + if git merge $other_parents >/dev/null 2>&1 + then + # Cleanly merges + continue + fi + if test -s "$GIT_DIR/MERGE_RR" + then + git show -s --pretty=format:"Learning from %h %s" "$commit" + git rerere + git checkout -q $commit -- . + git rerere + fi + git reset -q --hard +done + +if test -z "$branch" +then + git checkout "$original_HEAD" +else + git checkout "${branch#refs/heads/}" +fi -- cgit v1.2.3 From 1d4c4986708688c14e72a5b442a3432048aeea79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 29 Sep 2008 22:08:14 +0200 Subject: remove vim syntax highlighting in favor of upstream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As of version 7.2, vim ships with its own syntax highlighting for git commit messages, which is: 1. more comprehensive in splitting up the various components of the file 2. in accordance with the usual vim behavior for syntax highlighting (e.g., respecting b:current_syntax) 3. presumably better maintained (I have not been using what's in git's contrib/ directory for some time in favor of the upstream version) Furthermore, vim upsream also provides syntax highlighting for other git filetypes (gitconfig, rebase, send-email). This patch gets rid of our local version and just points interested parties to the upstream version. The code for auto-detecting filetypes is taken from vim's runtime/filetype.vim. Signed-off-by: SZEDER Gábor Signed-off-by: Jeff King Signed-off-by: Shawn O. Pearce --- contrib/vim/README | 38 ++++++++++++++++++++++++++++++-------- contrib/vim/syntax/gitcommit.vim | 18 ------------------ 2 files changed, 30 insertions(+), 26 deletions(-) delete mode 100644 contrib/vim/syntax/gitcommit.vim (limited to 'contrib') diff --git a/contrib/vim/README b/contrib/vim/README index 9e7881fea9..c487346eba 100644 --- a/contrib/vim/README +++ b/contrib/vim/README @@ -1,8 +1,30 @@ -To syntax highlight git's commit messages, you need to: - 1. Copy syntax/gitcommit.vim to vim's syntax directory: - $ mkdir -p $HOME/.vim/syntax - $ cp syntax/gitcommit.vim $HOME/.vim/syntax - 2. Auto-detect the editing of git commit files: - $ cat >>$HOME/.vimrc <<'EOF' - autocmd BufNewFile,BufRead COMMIT_EDITMSG set filetype=gitcommit - EOF +Syntax highlighting for git commit messages, config files, etc. is +included with the vim distribution as of vim 7.2, and should work +automatically. + +If you have an older version of vim, you can get the latest syntax +files from the vim project: + + http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/git.vim + http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitcommit.vim + http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitconfig.vim + http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitrebase.vim + http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitsendemail.vim + +To install: + + 1. Copy these files to vim's syntax directory $HOME/.vim/syntax + 2. To auto-detect the editing of various git-related filetypes: + $ cat >>$HOME/.vim/filetype.vim <<'EOF' + autocmd BufNewFile,BufRead *.git/COMMIT_EDITMSG setf gitcommit + autocmd BufNewFile,BufRead *.git/config,.gitconfig setf gitconfig + autocmd BufNewFile,BufRead git-rebase-todo setf gitrebase + autocmd BufNewFile,BufRead .msg.[0-9]* + \ if getline(1) =~ '^From.*# This line is ignored.$' | + \ setf gitsendemail | + \ endif + autocmd BufNewFile,BufRead *.git/** + \ if getline(1) =~ '^\x\{40\}\>\|^ref: ' | + \ setf git | + \ endif + EOF diff --git a/contrib/vim/syntax/gitcommit.vim b/contrib/vim/syntax/gitcommit.vim deleted file mode 100644 index 332121b40e..0000000000 --- a/contrib/vim/syntax/gitcommit.vim +++ /dev/null @@ -1,18 +0,0 @@ -syn region gitLine start=/^#/ end=/$/ -syn region gitCommit start=/^# Changes to be committed:$/ end=/^#$/ contains=gitHead,gitCommitFile -syn region gitHead contained start=/^# (.*)/ end=/^#$/ -syn region gitChanged start=/^# Changed but not updated:/ end=/^#$/ contains=gitHead,gitChangedFile -syn region gitUntracked start=/^# Untracked files:/ end=/^#$/ contains=gitHead,gitUntrackedFile - -syn match gitCommitFile contained /^#\t.*/hs=s+2 -syn match gitChangedFile contained /^#\t.*/hs=s+2 -syn match gitUntrackedFile contained /^#\t.*/hs=s+2 - -hi def link gitLine Comment -hi def link gitCommit Comment -hi def link gitChanged Comment -hi def link gitHead Comment -hi def link gitUntracked Comment -hi def link gitCommitFile Type -hi def link gitChangedFile Constant -hi def link gitUntrackedFile Constant -- cgit v1.2.3 From c33912ae2463b9e486fb016ffa9d21ff8e62984a Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Tue, 30 Sep 2008 00:36:28 +0100 Subject: Add OS X support to the pre-auto-gc example hook Signed-off-by: Jonathan del Strother Acked-by: Miklos Vajna Signed-off-by: Shawn O. Pearce --- contrib/hooks/pre-auto-gc-battery | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery index 0096f57b7e..b0a8caae9e 100644 --- a/contrib/hooks/pre-auto-gc-battery +++ b/contrib/hooks/pre-auto-gc-battery @@ -1,9 +1,9 @@ #!/bin/sh # # An example hook script to verify if you are on battery, in case you -# are running Linux. Called by git-gc --auto with no arguments. The hook -# should exit with non-zero status after issuing an appropriate message -# if it wants to stop the auto repacking. +# are running Linux or OS X. Called by git-gc --auto with no arguments. +# The hook should exit with non-zero status after issuing an appropriate +# message if it wants to stop the auto repacking. # # This hook is stored in the contrib/hooks directory. Your distribution # may have put this somewhere else. If you want to use this hook, you @@ -28,6 +28,10 @@ elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null then exit 0 elif grep -q '0x01$' /proc/apm 2>/dev/null +then + exit 0 +elif test -x /usr/bin/pmset && /usr/bin/pmset -g batt | + grep -q "Currently drawing from 'AC Power'" then exit 0 fi -- cgit v1.2.3 From fd3a8dcbbd5972912cad44d7a5571d7606b8739e Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Sun, 28 Sep 2008 07:51:21 +0300 Subject: bash completion: Add --[no-]validate to "git send-email" Signed-off-by: Teemu Likonen Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 93f088189e..7284c3b5a8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1141,7 +1141,8 @@ _git_send_email () --no-suppress-from --no-thread --quiet --signed-off-by-cc --smtp-pass --smtp-server --smtp-server-port --smtp-ssl --smtp-user --subject - --suppress-cc --suppress-from --thread --to" + --suppress-cc --suppress-from --thread --to + --validate --no-validate" return ;; esac -- cgit v1.2.3 From 5a625b07bbff709037f26be1c393337b6d9e103f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 3 Oct 2008 21:34:49 +0200 Subject: bash: remove fetch, push, pull dashed form leftovers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't provide complation for git-commands in dashed form anymore, so there is no need to keep those cases. Signed-off-by: SZEDER Gábor Tested-by: Thomas Rast Signed-off-by: Shawn O. Pearce --- contrib/completion/git-completion.bash | 36 +++++++++------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 3bc45f6b47..751e273e1a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -799,14 +799,9 @@ _git_fetch () { local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[0]},$COMP_CWORD" in - git-fetch*,1) + if [ "$COMP_CWORD" = 2 ]; then __gitcomp "$(__git_remotes)" - ;; - git,2) - __gitcomp "$(__git_remotes)" - ;; - *) + else case "$cur" in *:*) local pfx="" @@ -825,8 +820,7 @@ _git_fetch () __gitcomp "$(__git_refs2 "$remote")" ;; esac - ;; - esac + fi } _git_format_patch () @@ -1063,36 +1057,25 @@ _git_pull () { local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[0]},$COMP_CWORD" in - git-pull*,1) - __gitcomp "$(__git_remotes)" - ;; - git,2) + if [ "$COMP_CWORD" = 2 ]; then __gitcomp "$(__git_remotes)" - ;; - *) + else local remote case "${COMP_WORDS[0]}" in git-pull) remote="${COMP_WORDS[1]}" ;; git) remote="${COMP_WORDS[2]}" ;; esac __gitcomp "$(__git_refs "$remote")" - ;; - esac + fi } _git_push () { local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[0]},$COMP_CWORD" in - git-push*,1) - __gitcomp "$(__git_remotes)" - ;; - git,2) + if [ "$COMP_CWORD" = 2 ]; then __gitcomp "$(__git_remotes)" - ;; - *) + else case "$cur" in *:*) local remote @@ -1116,8 +1099,7 @@ _git_push () __gitcomp "$(__git_refs)" ;; esac - ;; - esac + fi } _git_rebase () -- cgit v1.2.3 From 84ed4c5d117d72f02cc918e413b9861a9d2846d7 Mon Sep 17 00:00:00 2001 From: Miklos Vajna Date: Tue, 14 Oct 2008 16:42:45 +0200 Subject: Add Linux PPC support to the pre-auto-gc example hook Signed-off-by: Miklos Vajna Signed-off-by: Junio C Hamano --- contrib/hooks/pre-auto-gc-battery | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contrib') diff --git a/contrib/hooks/pre-auto-gc-battery b/contrib/hooks/pre-auto-gc-battery index b0a8caae9e..1f914c94aa 100644 --- a/contrib/hooks/pre-auto-gc-battery +++ b/contrib/hooks/pre-auto-gc-battery @@ -28,6 +28,9 @@ elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null then exit 0 elif grep -q '0x01$' /proc/apm 2>/dev/null +then + exit 0 +elif grep -q "AC Power \+: 1" /proc/pmu/info 2>/dev/null then exit 0 elif test -x /usr/bin/pmset && /usr/bin/pmset -g batt | -- cgit v1.2.3 From 6c2a6022dbc5879f5d6c267925408e484be6214a Mon Sep 17 00:00:00 2001 From: Mikael Magnusson Date: Fri, 19 Sep 2008 15:48:08 +0200 Subject: Typo "does not exists" when git remote update remote. --- contrib/examples/git-remote.perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/examples/git-remote.perl b/contrib/examples/git-remote.perl index 36bd54c985..b17952a785 100755 --- a/contrib/examples/git-remote.perl +++ b/contrib/examples/git-remote.perl @@ -309,7 +309,7 @@ sub update_remote { } } } else { - print STDERR "Remote group $name does not exists.\n"; + print STDERR "Remote group $name does not exist.\n"; exit(1); } for (@remotes) { -- cgit v1.2.3 From 3b1eb124932772daee09419a581d418ea2d50045 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 17 Oct 2008 21:41:18 -0500 Subject: contrib: update packinfo.pl to not use dashed commands Signed-off-by: Dan McGee Signed-off-by: Junio C Hamano --- contrib/stats/packinfo.pl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/stats/packinfo.pl b/contrib/stats/packinfo.pl index f4a7b62cd9..be188c0f11 100755 --- a/contrib/stats/packinfo.pl +++ b/contrib/stats/packinfo.pl @@ -1,9 +1,9 @@ #!/usr/bin/perl # # This tool will print vaguely pretty information about a pack. It -# expects the output of "git-verify-pack -v" as input on stdin. +# expects the output of "git verify-pack -v" as input on stdin. # -# $ git-verify-pack -v | packinfo.pl +# $ git verify-pack -v | packinfo.pl # # This prints some full-pack statistics; currently "all sizes", "all # path sizes", "tree sizes", "tree path sizes", and "depths". @@ -20,7 +20,7 @@ # # When run as: # -# $ git-verify-pack -v | packinfo.pl -tree +# $ git verify-pack -v | packinfo.pl -tree # # the trees of objects are output along with the stats. This looks # like: @@ -43,7 +43,7 @@ # # When run as: # -# $ git-verify-pack -v | packinfo.pl -tree -filenames +# $ git verify-pack -v | packinfo.pl -tree -filenames # # it adds filenames to the tree. Getting this information is slow: # @@ -58,7 +58,7 @@ # # When run as: # -# $ git-verify-pack -v | packinfo.pl -dump +# $ git verify-pack -v | packinfo.pl -dump # # it prints out "sha1 size pathsize depth" for each sha1 in lexical # order. @@ -106,7 +106,7 @@ while () { } if ($filenames && ($tree || $dump)) { - open(NAMES, "git-name-rev --all|"); + open(NAMES, "git name-rev --all|"); while () { if (/^(\S+)\s+(.*)$/) { my ($sha1, $name) = ($1, $2); @@ -117,7 +117,7 @@ if ($filenames && ($tree || $dump)) { for my $commit (@commits) { my $name = $names{$commit}; - open(TREE, "git-ls-tree -t -r $commit|"); + open(TREE, "git ls-tree -t -r $commit|"); print STDERR "Plumbing tree $name\n"; while () { if (/^(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) { -- cgit v1.2.3 From 99f0b59954c4682fc3145ba2c49f88ea20b30174 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 20 Oct 2008 11:31:38 -0600 Subject: bash completion: Add 'workflows' to 'git help' Completion for new workflow documentation introduced in f948dd8 Signed-off-by: Lee Marlow Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d192927c20..eebe73409b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -881,6 +881,7 @@ _git_help () attributes cli core-tutorial cvs-migration diffcore gitk glossary hooks ignore modules repository-layout tutorial tutorial-2 + workflows " } -- cgit v1.2.3 From 41d8cf7d7fd79fe1fd00b04052c49bffaedfd309 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Fri, 31 Oct 2008 01:04:46 +0100 Subject: bash completion: add doubledash to "git show" Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 751e273e1a..39a1ce5a39 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1410,6 +1410,8 @@ _git_shortlog () _git_show () { + __git_has_doubledash && return + local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --pretty=*) -- cgit v1.2.3 From 4471649f44d7a8e4b7b927e43b848bb71b75630d Mon Sep 17 00:00:00 2001 From: Pete Harlan Date: Mon, 3 Nov 2008 23:19:53 -0800 Subject: contrib/hooks/post-receive-email: Put rev display in separate function The display of a revision in an email-appropriate format is done in two places with similar code. In preparation for making that display more complex, move it into a separate function that handles both cases. Signed-off-by: Pete Harlan Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 41 +++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 41368950d6..2cd373d625 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -224,13 +224,7 @@ generate_create_branch_email() echo "" echo $LOGBEGIN - # This shows all log entries that are not already covered by - # another ref - i.e. commits that are now accessible from this - # ref that were previously not accessible - # (see generate_update_branch_email for the explanation of this - # command) - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $newrev + show_new_revisions echo $LOGEND } @@ -390,8 +384,7 @@ generate_update_branch_email() echo "" echo $LOGBEGIN - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $oldrev..$newrev + show_new_revisions # XXX: Need a way of detecting whether git rev-list actually # outputted anything, so that we can issue a "no new @@ -591,6 +584,36 @@ generate_delete_general_email() echo $LOGEND } + +# --------------- Miscellaneous utilities + +# +# Show new revisions as the user would like to see them in the email. +# +show_new_revisions() +{ + # This shows all log entries that are not already covered by + # another ref - i.e. commits that are now accessible from this + # ref that were previously not accessible + # (see generate_update_branch_email for the explanation of this + # command) + + # Revision range passed to rev-list differs for new vs. updated + # branches. + if [ "$change_type" = create ] + then + # Show all revisions exclusive to this (new) branch. + revspec=$newrev + else + # Branch update; show revisions not part of $oldrev. + revspec=$oldrev..$newrev + fi + + git rev-parse --not --branches | grep -v $(git rev-parse $refname) | + git rev-list --pretty --stdin $revspec +} + + send_mail() { if [ -n "$envelopesender" ]; then -- cgit v1.2.3 From b0a7d111732f644623253927c9ef2d4f3009e668 Mon Sep 17 00:00:00 2001 From: Pete Harlan Date: Mon, 3 Nov 2008 23:19:54 -0800 Subject: contrib/hooks/post-receive-email: Make revision display configurable Add configuration option hooks.showrev, letting the user override how revisions will be shown in the commit email. Signed-off-by: Pete Harlan Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 2cd373d625..28a3c0e46e 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -38,6 +38,12 @@ # hooks.emailprefix # All emails have their subjects prefixed with this prefix, or "[SCM]" # if emailprefix is unset, to aid filtering +# hooks.showrev +# The shell command used to format each revision in the email, with +# "%s" replaced with the commit id. Defaults to "git rev-list -1 +# --pretty %s", displaying the commit id, author, date and log +# message. To list full patches separated by a blank line, you +# could set this to "git show -C %s; echo". # # Notes # ----- @@ -610,7 +616,16 @@ show_new_revisions() fi git rev-parse --not --branches | grep -v $(git rev-parse $refname) | - git rev-list --pretty --stdin $revspec + if [ -z "$custom_showrev" ] + then + git rev-list --pretty --stdin $revspec + else + git rev-list --stdin $revspec | + while read onerev + do + eval $(printf "$custom_showrev" $onerev) + done + fi } @@ -650,6 +665,7 @@ recipients=$(git config hooks.mailinglist) announcerecipients=$(git config hooks.announcelist) envelopesender=$(git config hooks.envelopesender) emailprefix=$(git config hooks.emailprefix || echo '[SCM] ') +custom_showrev=$(git config hooks.showrev) # --- Main loop # Allow dual mode: run from the command line just like the update hook, or -- cgit v1.2.3 From 7f96e2e25aa008556a4ede7a65de8488eb9890e6 Mon Sep 17 00:00:00 2001 From: John Chapman Date: Sat, 8 Nov 2008 14:22:48 +1100 Subject: git-p4: Support purged files and optimize memory usage Purged files are handled as if they are merely deleted, which is not entirely optimal, but I don't know of any other way to handle them. File data is deleted from memory as early as they can, and they are more efficiently handled, at (significant) cost to CPU usage. Still need to handle p4 branches with spaces in their names. Still need to make git-p4 clone more reliable. - Perhaps with a --continue option. (Sometimes the p4 server kills the connection) Signed-off-by: John Chapman Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 2216cacba7..38d1a17333 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -946,7 +946,7 @@ class P4Sync(Command): if includeFile: filesForCommit.append(f) - if f['action'] != 'delete': + if f['action'] not in ('delete', 'purge'): filesToRead.append(f) filedata = [] @@ -965,11 +965,11 @@ class P4Sync(Command): while j < len(filedata): stat = filedata[j] j += 1 - text = []; + text = '' while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'): - text.append(filedata[j]['data']) + text += filedata[j]['data'] + del filedata[j]['data'] j += 1 - text = ''.join(text) if not stat.has_key('depotFile'): sys.stderr.write("p4 print fails with: %s\n" % repr(stat)) @@ -1038,7 +1038,7 @@ class P4Sync(Command): continue relPath = self.stripRepoPath(file['path'], branchPrefixes) - if file["action"] == "delete": + if file["action"] in ("delete", "purge"): self.gitStream.write("D %s\n" % relPath) else: data = file['data'] @@ -1077,7 +1077,7 @@ class P4Sync(Command): cleanedFiles = {} for info in files: - if info["action"] == "delete": + if info["action"] in ("delete", "purge"): continue cleanedFiles[info["depotFile"]] = info["rev"] @@ -1400,7 +1400,7 @@ class P4Sync(Command): if change > newestRevision: newestRevision = change - if info["action"] == "delete": + if info["action"] in ("delete", "purge"): # don't increase the file cnt, otherwise details["depotFile123"] will have gaps! #fileCnt = fileCnt + 1 continue -- cgit v1.2.3 From 36bd844658cf244ec2c6756c18673a4b7ed8ec9e Mon Sep 17 00:00:00 2001 From: John Chapman Date: Sat, 8 Nov 2008 14:22:49 +1100 Subject: git-p4: Cache git config for performance This makes git-p4 noticibly faster on Windows. Signed-off-by: John Chapman Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 38d1a17333..9f0a5f92c1 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -316,8 +316,11 @@ def gitBranchExists(branch): stderr=subprocess.PIPE, stdout=subprocess.PIPE); return proc.wait() == 0; +_gitConfig = {} def gitConfig(key): - return read_pipe("git config %s" % key, ignore_error=True).strip() + if not _gitConfig.has_key(key): + _gitConfig[key] = read_pipe("git config %s" % key, ignore_error=True).strip() + return _gitConfig[key] def p4BranchesInGit(branchesAreInRemotes = True): branches = {} -- cgit v1.2.3 From 36d2078ff19c849aef94f045bf2b4fb73a5098e8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 1 Nov 2008 20:34:33 +0100 Subject: git.el: Improve error handling for commits. Display all errors happening in the various subcommands of the commit sequence, and abort on any error. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index c1cf1cbcc0..d357b543e2 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -208,6 +208,15 @@ and returns the process output as a string, or nil if the git failed." (and (eq 0 (apply #' git-call-process-env t env args)) (buffer-string)))) +(defun git-call-process-string-display-error (&rest args) + "Wrapper for call-process that displays error message and returns +the process output as a string, or nil if the git command failed." + (with-temp-buffer + (if (eq 0 (apply #'git-call-process-env (list t t) nil args)) + (buffer-string) + (display-message-or-buffer (current-buffer)) + nil))) + (defun git-run-process-region (buffer start end program args) "Run a git process with a buffer region as input." (let ((output-buffer (current-buffer)) @@ -391,14 +400,16 @@ and returns the process output as a string, or nil if the git failed." (defun git-read-tree (tree &optional index-file) "Read a tree into the index file." - (apply #'git-call-process-env nil - (if index-file `(("GIT_INDEX_FILE" . ,index-file)) nil) - "read-tree" (if tree (list tree)))) + (let ((process-environment + (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment))) + (apply 'git-call-process-display-error "read-tree" (if tree (list tree))))) (defun git-write-tree (&optional index-file) "Call git-write-tree and return the resulting tree SHA1 as a string." - (git-get-string-sha1 - (git-call-process-env-string (and index-file `(("GIT_INDEX_FILE" . ,index-file))) "write-tree"))) + (let ((process-environment + (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) process-environment))) + (git-get-string-sha1 + (git-call-process-string-display-error "write-tree")))) (defun git-commit-tree (buffer tree head) "Call git-commit-tree with buffer as input and return the resulting commit SHA1." @@ -824,19 +835,18 @@ Return the list of files that haven't been handled." (defun git-update-index (index-file files) "Run git-update-index on a list of files." - (let ((env (and index-file `(("GIT_INDEX_FILE" . ,index-file)))) + (let ((process-environment (append (and index-file (list (concat "GIT_INDEX_FILE=" index-file))) + process-environment)) added deleted modified) (dolist (info files) (case (git-fileinfo->state info) ('added (push info added)) ('deleted (push info deleted)) ('modified (push info modified)))) - (when added - (apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added))) - (when deleted - (apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted))) - (when modified - (apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified))))) + (and + (or (not added) (apply #'git-call-process-display-error "update-index" "--add" "--" (git-get-filenames added))) + (or (not deleted) (apply #'git-call-process-display-error "update-index" "--remove" "--" (git-get-filenames deleted))) + (or (not modified) (apply #'git-call-process-display-error "update-index" "--" (git-get-filenames modified)))))) (defun git-run-pre-commit-hook () "Run the pre-commit hook if any." @@ -862,17 +872,19 @@ Return the list of files that haven't been handled." (message "You cannot commit unmerged files, resolve them first.") (unwind-protect (let ((files (git-marked-files-state 'added 'deleted 'modified)) - head head-tree) + head tree head-tree) (unless (git-empty-db-p) (setq head (git-rev-parse "HEAD") head-tree (git-rev-parse "HEAD^{tree}"))) (if files (progn (message "Running git commit...") - (git-read-tree head-tree index-file) - (git-update-index nil files) ;update both the default index - (git-update-index index-file files) ;and the temporary one - (let ((tree (git-write-tree index-file))) + (when + (and + (git-read-tree head-tree index-file) + (git-update-index nil files) ;update both the default index + (git-update-index index-file files) ;and the temporary one + (setq tree (git-write-tree index-file))) (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))) -- cgit v1.2.3 From 9ddf6d7c105e58d76ea6762d8cc8eebdf463903e Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 1 Nov 2008 20:42:39 +0100 Subject: git.el: Remove the env parameter in git-call-process and git-call-process-string. All callers that need to change the environment now set process-environment themselves. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 54 +++++++++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 28 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d357b543e2..d28b45e558 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -183,11 +183,9 @@ if there is already one that displays the same directory." "Build a list of NAME=VALUE strings from a list of environment strings." (mapcar (lambda (entry) (concat (car entry) "=" (cdr entry))) env)) -(defun git-call-process-env (buffer env &rest args) +(defun git-call-process (buffer &rest args) "Wrapper for call-process that sets environment strings." - (let ((process-environment (append (git-get-env-strings env) - process-environment))) - (apply #'call-process "git" nil buffer nil args))) + (apply #'call-process "git" nil buffer nil args)) (defun git-call-process-display-error (&rest args) "Wrapper for call-process that displays error messages." @@ -197,22 +195,22 @@ if there is already one that displays the same directory." (let ((default-directory dir) (buffer-read-only nil)) (erase-buffer) - (eq 0 (apply 'call-process "git" nil (list buffer t) nil args)))))) + (eq 0 (apply #'git-call-process (list buffer t) args)))))) (unless ok (display-message-or-buffer buffer)) ok)) -(defun git-call-process-env-string (env &rest args) - "Wrapper for call-process that sets environment strings, -and returns the process output as a string, or nil if the git failed." +(defun git-call-process-string (&rest args) + "Wrapper for call-process that returns the process output as a string, +or nil if the git command failed." (with-temp-buffer - (and (eq 0 (apply #' git-call-process-env t env args)) + (and (eq 0 (apply #'git-call-process t args)) (buffer-string)))) (defun git-call-process-string-display-error (&rest args) "Wrapper for call-process that displays error message and returns the process output as a string, or nil if the git command failed." (with-temp-buffer - (if (eq 0 (apply #'git-call-process-env (list t t) nil args)) + (if (eq 0 (apply #'git-call-process (list t t) args)) (buffer-string) (display-message-or-buffer (current-buffer)) nil))) @@ -235,7 +233,7 @@ the process output as a string, or nil if the git command failed." (let ((default-directory dir) (buffer-read-only nil)) (erase-buffer) - (apply #'git-call-process-env buffer nil args))) + (apply #'git-call-process buffer args))) (message "Running git %s...done" (car args)) buffer)) @@ -336,7 +334,7 @@ the process output as a string, or nil if the git command failed." (let ((cdup (with-output-to-string (with-current-buffer standard-output (cd dir) - (unless (eq 0 (call-process "git" nil t nil "rev-parse" "--show-cdup")) + (unless (eq 0 (git-call-process t "rev-parse" "--show-cdup")) (error "cannot find top-level git tree for %s." dir)))))) (expand-file-name (concat (file-name-as-directory dir) (car (split-string cdup "\n")))))) @@ -357,7 +355,7 @@ the process output as a string, or nil if the git command failed." (sort-lines nil (point-min) (point-max)) (save-buffer)) (when created - (git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name))) + (git-call-process nil "update-index" "--add" "--" (file-relative-name ignore-name))) (git-update-status-files (list (file-relative-name ignore-name)) 'unknown))) ; propertize definition for XEmacs, stolen from erc-compat @@ -376,16 +374,16 @@ the process output as a string, or nil if the git command failed." (defun git-rev-parse (rev) "Parse a revision name and return its SHA1." (git-get-string-sha1 - (git-call-process-env-string nil "rev-parse" rev))) + (git-call-process-string "rev-parse" rev))) (defun git-config (key) "Retrieve the value associated to KEY in the git repository config file." - (let ((str (git-call-process-env-string nil "config" key))) + (let ((str (git-call-process-string "config" key))) (and str (car (split-string str "\n"))))) (defun git-symbolic-ref (ref) "Wrapper for the git-symbolic-ref command." - (let ((str (git-call-process-env-string nil "symbolic-ref" ref))) + (let ((str (git-call-process-string "symbolic-ref" ref))) (and str (car (split-string str "\n"))))) (defun git-update-ref (ref newval &optional oldval reason) @@ -463,7 +461,7 @@ the process output as a string, or nil if the git command failed." (defun git-empty-db-p () "Check if the git db is empty (no commit done yet)." - (not (eq 0 (call-process "git" nil nil nil "rev-parse" "--verify" "HEAD")))) + (not (eq 0 (git-call-process nil "rev-parse" "--verify" "HEAD")))) (defun git-get-merge-heads () "Retrieve the merge heads from the MERGE_HEAD file if present." @@ -479,7 +477,7 @@ the process output as a string, or nil if the git command failed." (defun git-get-commit-description (commit) "Get a one-line description of COMMIT." (let ((coding-system-for-read (git-get-logoutput-coding-system))) - (let ((descr (git-call-process-env-string nil "log" "--max-count=1" "--pretty=oneline" commit))) + (let ((descr (git-call-process-string "log" "--max-count=1" "--pretty=oneline" commit))) (if (and descr (string-match "\\`\\([0-9a-f]\\{40\\}\\) *\\(.*\\)$" descr)) (concat (substring (match-string 1 descr) 0 10) " - " (match-string 2 descr)) descr)))) @@ -655,7 +653,7 @@ Return the list of files that haven't been handled." (let ((remaining (copy-sequence files)) infolist) (with-temp-buffer - (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files) + (apply #'git-call-process t "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) (while (re-search-forward ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0" @@ -688,7 +686,7 @@ Return the list of files that haven't been handled." Return the list of files that haven't been handled." (let (infolist) (with-temp-buffer - (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files)) + (apply #'git-call-process t "ls-files" "-z" (append options (list "--") files)) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*?\\)\\(/?\\)\0" nil t 1) (let ((name (match-string 1))) @@ -705,7 +703,7 @@ Return the list of files that haven't been handled." (let ((remaining (copy-sequence files)) infolist) (with-temp-buffer - (apply #'git-call-process-env t nil "ls-files" "-z" "-s" "-c" "--" files) + (apply #'git-call-process t "ls-files" "-z" "-s" "-c" "--" files) (goto-char (point-min)) (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t) (let* ((new-perm (string-to-number (match-string 1) 8)) @@ -719,7 +717,7 @@ Return the list of files that haven't been handled." (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." (with-temp-buffer - (apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files) + (apply #'git-call-process t "ls-files" "-z" "-u" "--" files) (goto-char (point-min)) (let (unmerged-files) (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) @@ -893,8 +891,8 @@ Return the list of files that haven't been handled." (condition-case nil (delete-file ".git/MERGE_MSG") (error nil)) (with-current-buffer buffer (erase-buffer)) (git-update-status-files (git-get-filenames files) 'uptodate) - (git-call-process-env nil nil "rerere") - (git-call-process-env nil nil "gc" "--auto") + (git-call-process nil "rerere") + (git-call-process nil "gc" "--auto") (git-refresh-files) (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) @@ -1311,7 +1309,7 @@ Return the list of files that haven't been handled." (let (author-name author-email subject date msg) (with-temp-buffer (let ((coding-system (git-get-logoutput-coding-system))) - (git-call-process-env t nil "log" "-1" "--pretty=medium" commit) + (git-call-process t "log" "-1" "--pretty=medium" commit) (goto-char (point-min)) (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t) (setq author-name (match-string 1)) @@ -1331,7 +1329,7 @@ Return the list of files that haven't been handled." "Retrieve the list of files modified by COMMIT." (let (files) (with-temp-buffer - (git-call-process-env t nil "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit) + (git-call-process t "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (push (match-string 1) files))) @@ -1395,7 +1393,7 @@ amended version of it." (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) (unless status (error "Not in git-status buffer.")) (message "Refreshing git status...") - (git-call-process-env nil nil "update-index" "--refresh") + (git-call-process nil "update-index" "--refresh") (git-clear-status status) (git-update-status-files nil) ; restore file marks @@ -1588,7 +1586,7 @@ Meant to be used in `after-save-hook'." (let ((filename (file-relative-name file dir))) ; skip files located inside the .git directory (unless (string-match "^\\.git/" filename) - (git-call-process-env nil nil "add" "--refresh" "--" filename) + (git-call-process nil "add" "--refresh" "--" filename) (git-update-status-files (list filename) 'uptodate))))))) (defun git-help () -- cgit v1.2.3 From 6fb204266cc985284b554e9f9f1894ec8360b2f5 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 2 Aug 2008 18:04:31 +0200 Subject: git.el: Simplify handling of merge heads in the commit log-edit buffer. Use a single Merge: header instead of one Parent: header for each parent, and don't list the current HEAD as a merged head. Support symbolic references too. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index d28b45e558..53ada39b43 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -173,7 +173,7 @@ if there is already one that displays the same directory." (defconst git-log-msg-separator "--- log message follows this line ---") (defvar git-log-edit-font-lock-keywords - `(("^\\(Author:\\|Date:\\|Parent:\\|Signed-off-by:\\)\\(.*\\)$" + `(("^\\(Author:\\|Date:\\|Merge:\\|Signed-off-by:\\)\\(.*\\)$" (1 font-lock-keyword-face) (2 font-lock-function-name-face)) (,(concat "^\\(" (regexp-quote git-log-msg-separator) "\\)$") @@ -433,11 +433,11 @@ the process output as a string, or nil if the git command failed." (when (re-search-forward "^Date: +\\(.*\\)$" nil t) (setq author-date (match-string 1))) (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): ") + (when (re-search-forward "^Merge: +\\(.*\\)" nil t) + (setq subject "commit (merge): ") + (dolist (parent (split-string (match-string 1) " +" t)) (push "-p" args) - (push (match-string 1) args)))) + (push parent args)))) (setq log-start (point-min))) (setq log-end (point-max)) (goto-char log-start) @@ -1253,9 +1253,8 @@ Return the list of files that haven't been handled." (or author-email committer-email) (if date (format "Date: %s\n" date) "") (if merge-heads - (format "Parent: %s\n%s\n" - (git-rev-parse "HEAD") - (mapconcat (lambda (str) (concat "Parent: " str)) merge-heads "\n")) + (format "Merge: %s\n" + (mapconcat 'identity merge-heads " ")) "")) 'face 'git-header-face) (propertize git-log-msg-separator 'face 'git-separator-face) -- cgit v1.2.3 From ef5133df7c9d0304ce2fc437abc74c220f49dabd Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 2 Aug 2008 18:05:58 +0200 Subject: git.el: Properly handle merge commits in git-amend-commit. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 53ada39b43..207ff0ba27 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1235,11 +1235,10 @@ Return the list of files that haven't been handled." (goto-char (point-max)) (insert sign-off "\n")))) -(defun git-setup-log-buffer (buffer &optional author-name author-email subject date msg) +(defun git-setup-log-buffer (buffer &optional merge-heads author-name author-email subject date msg) "Setup the log buffer for a commit." (unless git-status (error "Not in git-status buffer.")) - (let ((merge-heads (git-get-merge-heads)) - (dir default-directory) + (let ((dir default-directory) (committer-name (git-get-committer-name)) (committer-email (git-get-committer-email)) (sign-off git-append-signed-off-by)) @@ -1294,7 +1293,7 @@ Return the list of files that haven't been handled." (goto-char (point-min)) (when (re-search-forward "^Date: \\(.*\\)$" nil t) (setq date (match-string 1))))) - (git-setup-log-buffer buffer author-name author-email subject date)) + (git-setup-log-buffer buffer (git-get-merge-heads) author-name author-email subject date)) (if (boundp 'log-edit-diff-function) (log-edit 'git-do-commit nil '((log-edit-listfun . git-log-edit-files) (log-edit-diff-function . git-log-edit-diff)) buffer) @@ -1305,11 +1304,13 @@ Return the list of files that haven't been handled." (defun git-setup-commit-buffer (commit) "Setup the commit buffer with the contents of COMMIT." - (let (author-name author-email subject date msg) + (let (parents author-name author-email subject date msg) (with-temp-buffer (let ((coding-system (git-get-logoutput-coding-system))) - (git-call-process t "log" "-1" "--pretty=medium" commit) + (git-call-process t "log" "-1" "--pretty=medium" "--abbrev=40" commit) (goto-char (point-min)) + (when (re-search-forward "^Merge: *\\(.*\\)$" nil t) + (setq parents (cdr (split-string (match-string 1) " +")))) (when (re-search-forward "^Author: *\\(.*\\) <\\(.*\\)>$" nil t) (setq author-name (match-string 1)) (setq author-email (match-string 2))) @@ -1321,14 +1322,14 @@ Return the list of files that haven't been handled." (setq subject (pop msg)) (while (and msg (zerop (length (car msg))) (pop msg))))) (git-setup-log-buffer (get-buffer-create "*git-commit*") - author-name author-email subject date + parents author-name author-email subject date (mapconcat #'identity msg "\n")))) (defun git-get-commit-files (commit) "Retrieve the list of files modified by COMMIT." (let (files) (with-temp-buffer - (git-call-process t "diff-tree" "-r" "-z" "--name-only" "--no-commit-id" commit) + (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" commit) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (push (match-string 1) files))) -- cgit v1.2.3 From db18a182a28f254fa514179dcd49ceb3d95cd9d8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 2 Aug 2008 20:35:20 +0200 Subject: git.el: Fix git-amend-commit to support amending an initial commit. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 207ff0ba27..2dd97eeb54 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -389,11 +389,12 @@ the process output as a string, or nil if the git command failed." (defun git-update-ref (ref newval &optional oldval reason) "Update a reference by calling git-update-ref." (let ((args (and oldval (list oldval)))) - (push newval args) + (when newval (push newval args)) (push ref args) (when reason (push reason args) (push "-m" args)) + (unless newval (push "-d" args)) (apply 'git-call-process-display-error "update-ref" args))) (defun git-read-tree (tree &optional index-file) @@ -1329,7 +1330,7 @@ Return the list of files that haven't been handled." "Retrieve the list of files modified by COMMIT." (let (files) (with-temp-buffer - (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" commit) + (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" "--root" commit) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (push (match-string 1) files))) @@ -1343,7 +1344,10 @@ amended version of it." (when (git-empty-db-p) (error "No commit to amend.")) (let* ((commit (git-rev-parse "HEAD")) (files (git-get-commit-files commit))) - (when (git-call-process-display-error "reset" "--soft" "HEAD^") + (when (if (git-rev-parse "HEAD^") + (git-call-process-display-error "reset" "--soft" "HEAD^") + (and (git-update-ref "ORIG_HEAD" commit) + (git-update-ref "HEAD" nil commit))) (git-update-status-files (copy-sequence files) 'uptodate) (git-mark-files git-status files) (git-refresh-files) -- cgit v1.2.3 From 433ee03f9748a8a5d0e0495bbf4b054ae922103b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 2 Aug 2008 20:35:52 +0200 Subject: git.el: Never clear the status buffer, only update the files. This makes it unnecessary to save/restore the file marks. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 169 ++++++++++++++++++++++++++------------------------- 1 file changed, 85 insertions(+), 84 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 2dd97eeb54..67c5275992 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -356,7 +356,7 @@ the process output as a string, or nil if the git command failed." (save-buffer)) (when created (git-call-process nil "update-index" "--add" "--" (file-relative-name ignore-name))) - (git-update-status-files (list (file-relative-name ignore-name)) 'unknown))) + (git-update-status-files (list (file-relative-name ignore-name))))) ; propertize definition for XEmacs, stolen from erc-compat (eval-when-compile @@ -497,14 +497,11 @@ the process output as a string, or nil if the git command failed." old-perm new-perm ;; permission flags rename-state ;; rename or copy state orig-name ;; original name for renames or copies + needs-update ;; whether file needs to be updated needs-refresh) ;; whether file needs to be refreshed (defvar git-status nil) -(defun git-clear-status (status) - "Remove everything from the status list." - (ewoc-filter status (lambda (info) nil))) - (defun git-set-fileinfo-state (info state) "Set the state of a file info." (unless (eq (git-fileinfo->state info) state) @@ -512,6 +509,7 @@ the process output as a string, or nil if the git command failed." (git-fileinfo->new-perm info) (git-fileinfo->old-perm info) (git-fileinfo->rename-state info) nil (git-fileinfo->orig-name info) nil + (git-fileinfo->needs-update info) nil (git-fileinfo->needs-refresh info) t))) (defun git-status-filenames-map (status func files &rest args) @@ -521,10 +519,11 @@ the process output as a string, or nil if the git command failed." (let ((file (pop files)) (node (ewoc-nth status 0))) (while (and file node) - (let ((info (ewoc-data node))) - (if (string-lessp (git-fileinfo->name info) file) + (let* ((info (ewoc-data node)) + (name (git-fileinfo->name info))) + (if (string-lessp name file) (setq node (ewoc-next status node)) - (if (string-equal (git-fileinfo->name info) file) + (if (string-equal name file) (apply func info args)) (setq file (pop files)))))))) @@ -622,37 +621,50 @@ the process output as a string, or nil if the git command failed." (git-file-type-as-string old-perm new-perm) (git-rename-as-string info))))) -(defun git-insert-info-list (status infolist) - "Insert a list of file infos in the status buffer, replacing existing ones if any." - (setq infolist (sort infolist - (lambda (info1 info2) - (string-lessp (git-fileinfo->name info1) - (git-fileinfo->name info2))))) - (let ((info (pop infolist)) - (node (ewoc-nth status 0))) +(defun git-update-node-fileinfo (node info) + "Update the fileinfo of the specified node. The names are assumed to match already." + (let ((data (ewoc-data node))) + (setf + ;; preserve the marked flag + (git-fileinfo->marked info) (git-fileinfo->marked data) + (git-fileinfo->needs-update data) nil) + (when (not (equal info data)) + (setf (git-fileinfo->needs-refresh info) t + (ewoc-data node) info)))) + +(defun git-insert-info-list (status infolist files) + "Insert a sorted list of file infos in the status buffer, replacing existing ones if any." + (let* ((info (pop infolist)) + (node (ewoc-nth status 0)) + (name (and info (git-fileinfo->name info))) + remaining) (while info - (cond ((not node) - (setq node (ewoc-enter-last status info)) - (setq info (pop infolist))) - ((string-lessp (git-fileinfo->name (ewoc-data node)) - (git-fileinfo->name info)) - (setq node (ewoc-next status node))) - ((string-equal (git-fileinfo->name (ewoc-data node)) - (git-fileinfo->name info)) - ;; preserve the marked flag - (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))) - (setf (git-fileinfo->needs-refresh info) t) - (setf (ewoc-data node) info) - (setq info (pop infolist))) - (t - (setq node (ewoc-enter-before status node info)) - (setq info (pop infolist))))))) + (let ((nodename (and node (git-fileinfo->name (ewoc-data node))))) + (while (and files (string-lessp (car files) name)) + (push (pop files) remaining)) + (when (and files (string-equal (car files) name)) + (setq files (cdr files))) + (cond ((not nodename) + (setq node (ewoc-enter-last status info)) + (setq info (pop infolist)) + (setq name (and info (git-fileinfo->name info)))) + ((string-lessp nodename name) + (setq node (ewoc-next status node))) + ((string-equal nodename name) + ;; preserve the marked flag + (git-update-node-fileinfo node info) + (setq info (pop infolist)) + (setq name (and info (git-fileinfo->name info)))) + (t + (setq node (ewoc-enter-before status node info)) + (setq info (pop infolist)) + (setq name (and info (git-fileinfo->name info))))))) + (nconc (nreverse remaining) files))) (defun git-run-diff-index (status files) "Run git-diff-index on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let ((remaining (copy-sequence files)) - infolist) + (let (infolist) (with-temp-buffer (apply #'git-call-process t "diff-index" "-z" "-M" "HEAD" "--" files) (goto-char (point-min)) @@ -669,11 +681,12 @@ Return the list of files that haven't been handled." (push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist) (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist) (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist)) - (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist)) - (setq remaining (delete name remaining)) - (when new-name (setq remaining (delete new-name remaining)))))) - (git-insert-info-list status infolist) - remaining)) + (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))))) + (setq infolist (sort (nreverse infolist) + (lambda (info1 info2) + (string-lessp (git-fileinfo->name info1) + (git-fileinfo->name info2))))) + (git-insert-info-list status infolist files))) (defun git-find-status-file (status file) "Find a given file in the status ewoc and return its node." @@ -693,16 +706,14 @@ Return the list of files that haven't been handled." (let ((name (match-string 1))) (push (git-create-fileinfo default-state name 0 (if (string-equal "/" (match-string 2)) (lsh ?\110 9) 0)) - infolist) - (setq files (delete name files))))) - (git-insert-info-list status infolist) - files)) + infolist)))) + (setq infolist (nreverse infolist)) ;; assume it is sorted already + (git-insert-info-list status infolist files))) (defun git-run-ls-files-cached (status files default-state) "Run git-ls-files -c on FILES and parse the results into STATUS. Return the list of files that haven't been handled." - (let ((remaining (copy-sequence files)) - infolist) + (let (infolist) (with-temp-buffer (apply #'git-call-process t "ls-files" "-z" "-s" "-c" "--" files) (goto-char (point-min)) @@ -710,10 +721,9 @@ Return the list of files that haven't been handled." (let* ((new-perm (string-to-number (match-string 1) 8)) (old-perm (if (eq default-state 'added) 0 new-perm)) (name (match-string 2))) - (push (git-create-fileinfo default-state name old-perm new-perm) infolist) - (setq remaining (delete name remaining))))) - (git-insert-info-list status infolist) - remaining)) + (push (git-create-fileinfo default-state name old-perm new-perm) infolist)))) + (setq infolist (nreverse infolist)) ;; assume it is sorted already + (git-insert-info-list status infolist files))) (defun git-run-ls-unmerged (status files) "Run git-ls-files -u on FILES and parse the results into STATUS." @@ -742,11 +752,17 @@ Return the list of files that haven't been handled." (concat "--exclude-per-directory=" git-per-dir-ignore-file) (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) -(defun git-update-status-files (files &optional default-state) +(defun git-update-status-files (&optional files) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) - (when (or git-show-uptodate files) - (git-run-ls-files-cached git-status files 'uptodate)) + ;; set the needs-update flag on existing files + (if (setq files (sort files #'string-lessp)) + (git-status-filenames-map + git-status (lambda (info) (setf (git-fileinfo->needs-update info) t)) files) + (ewoc-map (lambda (info) (setf (git-fileinfo->needs-update info) t) nil) git-status) + (git-call-process nil "update-index" "--refresh") + (when git-show-uptodate + (git-run-ls-files-cached git-status nil 'uptodate))) (let* ((remaining-files (if (git-empty-db-p) ; we need some special handling for an empty db (git-run-ls-files-cached git-status files 'added) @@ -756,7 +772,11 @@ Return the list of files that haven't been handled." (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o"))) (when (or remaining-files (and git-show-ignored (not files))) (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i"))) - (git-set-filenames-state git-status remaining-files default-state) + (unless files + (setq remaining-files (git-get-filenames (ewoc-collect git-status #'git-fileinfo->needs-update)))) + (when remaining-files + (setq remaining-files (git-run-ls-files-cached git-status remaining-files 'uptodate))) + (git-set-filenames-state git-status remaining-files nil) (git-refresh-files) (git-refresh-ewoc-hf git-status))) @@ -891,11 +911,9 @@ Return the list of files that haven't been handled." (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)) - (git-update-status-files (git-get-filenames files) 'uptodate) + (git-update-status-files (git-get-filenames files)) (git-call-process nil "rerere") (git-call-process nil "gc" "--auto") - (git-refresh-files) - (git-refresh-ewoc-hf git-status) (message "Committed %s." commit) (git-run-hook "post-commit" nil))) (message "Commit aborted.")))) @@ -1009,7 +1027,7 @@ Return the list of files that haven't been handled." (unless files (push (file-relative-name (read-file-name "File to add: " nil nil t)) files)) (when (apply 'git-call-process-display-error "update-index" "--add" "--" files) - (git-update-status-files files 'uptodate) + (git-update-status-files files) (git-success-message "Added" files)))) (defun git-ignore-file () @@ -1019,7 +1037,7 @@ Return the list of files that haven't been handled." (unless files (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files)) (dolist (f files) (git-append-to-ignore f)) - (git-update-status-files files 'ignored) + (git-update-status-files files) (git-success-message "Ignored" files))) (defun git-remove-file () @@ -1037,7 +1055,7 @@ Return the list of files that haven't been handled." (delete-directory name) (delete-file name)))) (when (apply 'git-call-process-display-error "update-index" "--remove" "--" files) - (git-update-status-files files nil) + (git-update-status-files files) (git-success-message "Removed" files))) (message "Aborting")))) @@ -1065,7 +1083,7 @@ Return the list of files that haven't been handled." (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added)) (or (not modified) (apply 'git-call-process-display-error "checkout" "HEAD" modified))))) - (git-update-status-files (append added modified) 'uptodate) + (git-update-status-files (append added modified)) (when ok (dolist (file modified) (let ((buffer (get-file-buffer file))) @@ -1078,7 +1096,7 @@ Return the list of files that haven't been handled." (let ((files (git-get-filenames (git-marked-files-state 'unmerged)))) (when files (when (apply 'git-call-process-display-error "update-index" "--" files) - (git-update-status-files files 'uptodate) + (git-update-status-files files) (git-success-message "Resolved" files))))) (defun git-remove-handled () @@ -1348,7 +1366,7 @@ amended version of it." (git-call-process-display-error "reset" "--soft" "HEAD^") (and (git-update-ref "ORIG_HEAD" commit) (git-update-ref "HEAD" nil commit))) - (git-update-status-files (copy-sequence files) 'uptodate) + (git-update-status-files (copy-sequence files)) (git-mark-files git-status files) (git-refresh-files) (git-setup-commit-buffer commit) @@ -1391,27 +1409,10 @@ amended version of it." (defun git-refresh-status () "Refresh the git status buffer." (interactive) - (let* ((status git-status) - (pos (ewoc-locate status)) - (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info))))) - (cur-name (and pos (git-fileinfo->name (ewoc-data pos))))) - (unless status (error "Not in git-status buffer.")) - (message "Refreshing git status...") - (git-call-process nil "update-index" "--refresh") - (git-clear-status status) - (git-update-status-files nil) - ; restore file marks - (when marked-files - (git-status-filenames-map status - (lambda (info) - (setf (git-fileinfo->marked info) t) - (setf (git-fileinfo->needs-refresh info) t)) - marked-files) - (git-refresh-files)) - ; move point to the current file name if any - (message "Refreshing git status...done") - (let ((node (and cur-name (git-find-status-file status cur-name)))) - (when node (ewoc-goto-node status node))))) + (unless git-status (error "Not in git-status buffer.")) + (message "Refreshing git status...") + (git-update-status-files) + (message "Refreshing git status...done")) (defun git-status-quit () "Quit git-status mode." @@ -1591,7 +1592,7 @@ Meant to be used in `after-save-hook'." ; skip files located inside the .git directory (unless (string-match "^\\.git/" filename) (git-call-process nil "add" "--refresh" "--" filename) - (git-update-status-files (list filename) 'uptodate))))))) + (git-update-status-files (list filename)))))))) (defun git-help () "Display help for Git mode." -- cgit v1.2.3 From b0a53e9e56d0a501aebc99d3614be413e91613f6 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 4 Aug 2008 09:30:42 +0200 Subject: git.el: Add an insert file command. This allows to insert a file in the buffer no matter what its state is, making it possible for instance to remove an up-to-date file. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 67c5275992..6119c31d05 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1019,6 +1019,11 @@ Return the list of files that haven't been handled." (setq node (ewoc-prev git-status node))) (ewoc-goto-node git-status last))) +(defun git-insert-file (file) + "Insert file(s) into the git-status buffer." + (interactive "fInsert file: ") + (git-update-status-files (list (file-relative-name file)))) + (defun git-add-file () "Add marked file(s) to the index cache." (interactive) @@ -1449,6 +1454,7 @@ amended version of it." (define-key map "\r" 'git-find-file) (define-key map "g" 'git-refresh-status) (define-key map "i" 'git-ignore-file) + (define-key map "I" 'git-insert-file) (define-key map "l" 'git-log-file) (define-key map "m" 'git-mark-file) (define-key map "M" 'git-mark-all) @@ -1505,6 +1511,7 @@ amended version of it." ["Revert File" git-revert-file t] ["Ignore File" git-ignore-file t] ["Remove File" git-remove-file t] + ["Insert File" git-insert-file t] "--------" ["Find File" git-find-file t] ["View File" git-view-file t] -- cgit v1.2.3 From c4e8b72f228b20d3ed6cfba0586364ea8ca431af Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 1 Nov 2008 20:14:10 +0100 Subject: git.el: Add possibility to mark files directly in git-update-status-files. This avoids the need to go through the list twice, which helps performance on large file lists. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 6119c31d05..9e9101b17e 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -752,7 +752,7 @@ Return the list of files that haven't been handled." (concat "--exclude-per-directory=" git-per-dir-ignore-file) (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) -(defun git-update-status-files (&optional files) +(defun git-update-status-files (&optional files mark-files) "Update the status of FILES from the index." (unless git-status (error "Not in git-status buffer.")) ;; set the needs-update flag on existing files @@ -777,12 +777,12 @@ Return the list of files that haven't been handled." (when remaining-files (setq remaining-files (git-run-ls-files-cached git-status remaining-files 'uptodate))) (git-set-filenames-state git-status remaining-files nil) + (when mark-files (git-mark-files git-status files)) (git-refresh-files) (git-refresh-ewoc-hf git-status))) (defun git-mark-files (status files) "Mark all the specified FILES, and unmark the others." - (setq files (sort files #'string-lessp)) (let ((file (and files (pop files))) (node (ewoc-nth status 0))) (while node @@ -1371,9 +1371,7 @@ amended version of it." (git-call-process-display-error "reset" "--soft" "HEAD^") (and (git-update-ref "ORIG_HEAD" commit) (git-update-ref "HEAD" nil commit))) - (git-update-status-files (copy-sequence files)) - (git-mark-files git-status files) - (git-refresh-files) + (git-update-status-files files t) (git-setup-commit-buffer commit) (git-commit-file)))) -- cgit v1.2.3 From 1905a8666a676f7070ba15c6f56f98bb1da20f7b Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 7 Nov 2008 14:28:09 +0100 Subject: git.el: Allow to commit even if there are no marked files. This can be useful to commit a merge that didn't result in any changes. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 9e9101b17e..09e8bae3a4 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -895,29 +895,26 @@ Return the list of files that haven't been handled." (unless (git-empty-db-p) (setq head (git-rev-parse "HEAD") head-tree (git-rev-parse "HEAD^{tree}"))) - (if files - (progn - (message "Running git commit...") - (when - (and - (git-read-tree head-tree index-file) - (git-update-index nil files) ;update both the default index - (git-update-index index-file files) ;and the temporary one - (setq tree (git-write-tree index-file))) - (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))) - (when commit - (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)) - (git-update-status-files (git-get-filenames files)) - (git-call-process nil "rerere") - (git-call-process nil "gc" "--auto") - (message "Committed %s." commit) - (git-run-hook "post-commit" nil))) - (message "Commit aborted.")))) - (message "No files to commit."))) + (message "Running git commit...") + (when + (and + (git-read-tree head-tree index-file) + (git-update-index nil files) ;update both the default index + (git-update-index index-file files) ;and the temporary one + (setq tree (git-write-tree index-file))) + (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))) + (when commit + (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)) + (git-update-status-files (git-get-filenames files)) + (git-call-process nil "rerere") + (git-call-process nil "gc" "--auto") + (message "Committed %s." commit) + (git-run-hook "post-commit" nil))) + (message "Commit aborted.")))) (delete-file index-file)))))) -- cgit v1.2.3 From 3d51c853df5730212f704a526340a5a059dffeda Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Wed, 26 Nov 2008 13:52:15 -0500 Subject: git-p4: fix keyword-expansion regex This text: my $dir = $File::Find::dir; return if ($dir !~ m,$options->{dirpat}$,); was improperly converted to: my $dir = $File$dir !~ m,$options->{dirpat}$,); by the keyword identifier expansion code. Add a \n to make sure the regex doesn't go across end-of-line boundaries. Signed-off-by: Pete Wyckoff Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 46136d49bf..2b122d3f51 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -963,7 +963,7 @@ class P4Sync(Command): if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'): text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text) elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'): - text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$',r'$\1$', text) + text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text) contents[stat['depotFile']] = text -- cgit v1.2.3 From 8d8163f377829d5f61f6053bd55fdcecaf360d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Thu, 27 Nov 2008 14:35:38 +0100 Subject: bash: remove dashed command leftovers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 5a625b07 (bash: remove fetch, push, pull dashed form leftovers, 2008-10-03) did that already, but there were still some git-cmd left here and there. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 41 +++++++--------------------------- 1 file changed, 8 insertions(+), 33 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 39a1ce5a39..bec09bdd62 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -650,21 +650,12 @@ _git_branch () _git_bundle () { - local mycword="$COMP_CWORD" - case "${COMP_WORDS[0]}" in - git) - local cmd="${COMP_WORDS[2]}" - mycword="$((mycword-1))" - ;; - git-bundle*) - local cmd="${COMP_WORDS[1]}" - ;; - esac - case "$mycword" in - 1) + local cmd="${COMP_WORDS[2]}" + case "$COMP_CWORD" in + 2) __gitcomp "create list-heads verify unbundle" ;; - 2) + 3) # looking for a file ;; *) @@ -812,12 +803,7 @@ _git_fetch () __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}" ;; *) - local remote - case "${COMP_WORDS[0]}" in - git-fetch) remote="${COMP_WORDS[1]}" ;; - git) remote="${COMP_WORDS[2]}" ;; - esac - __gitcomp "$(__git_refs2 "$remote")" + __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")" ;; esac fi @@ -1060,12 +1046,7 @@ _git_pull () if [ "$COMP_CWORD" = 2 ]; then __gitcomp "$(__git_remotes)" else - local remote - case "${COMP_WORDS[0]}" in - git-pull) remote="${COMP_WORDS[1]}" ;; - git) remote="${COMP_WORDS[2]}" ;; - esac - __gitcomp "$(__git_refs "$remote")" + __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" fi } @@ -1078,19 +1059,13 @@ _git_push () else case "$cur" in *:*) - local remote - case "${COMP_WORDS[0]}" in - git-push) remote="${COMP_WORDS[1]}" ;; - git) remote="${COMP_WORDS[2]}" ;; - esac - local pfx="" case "$COMP_WORDBREAKS" in *:*) : great ;; *) pfx="${cur%%:*}:" ;; esac - __gitcomp "$(__git_refs "$remote")" "$pfx" "${cur#*:}" + __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}" ;; +*) __gitcomp "$(__git_refs)" + "${cur#+}" @@ -1591,7 +1566,7 @@ _git_tag () -m|-F) COMPREPLY=() ;; - -*|tag|git-tag) + -*|tag) if [ $f = 1 ]; then __gitcomp "$(__git_tags)" else -- cgit v1.2.3 From 608efb875f89a946d5cb37b2dd4077132618e0e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 28 Nov 2008 01:46:38 +0100 Subject: bash: complete full refs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes it's handy to complete full refs, e.g. the user has some refs outside of refs/{heads,remotes,tags} or the user wants to complete some git command's special refs (like 'git show refs/bisect/bad'). To do that, we check whether the ref to be completed starts with 'refs/' or is 'refs' (to reduce the risk of matching 'refs-'). If it does, then we offer full refs for completion; otherwise everything works as usual. This way the impact on the common case is fairly small (hopefully not many users have branches or tags starting with 'refs'), and in the special case the cost of typing out 'refs' is bearable. While at it, also remove the unused 'cmd' variable from '__git_refs'. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index de193ba7c1..5fb34c49dc 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -188,11 +188,22 @@ __git_tags () __git_refs () { - local cmd i is_hash=y dir="$(__gitdir "$1")" + local i is_hash=y dir="$(__gitdir "$1")" + local cur="${COMP_WORDS[COMP_CWORD]}" format refs if [ -d "$dir" ]; then - if [ -e "$dir/HEAD" ]; then echo HEAD; fi - git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ - refs/tags refs/heads refs/remotes + case "$cur" in + refs|refs/*) + format="refname" + refs="${cur%/*}" + ;; + *) + if [ -e "$dir/HEAD" ]; then echo HEAD; fi + format="refname:short" + refs="refs/tags refs/heads refs/remotes" + ;; + esac + git --git-dir="$dir" for-each-ref --format="%($format)" \ + $refs return fi for i in $(git ls-remote "$dir" 2>/dev/null); do -- cgit v1.2.3 From c07838371b116251b6c4bc62a2ba64109baf74f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Thu, 27 Nov 2008 14:35:53 +0100 Subject: bash: offer refs instead of filenames for 'git revert' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The completion script for 'git revert' currently offers options and filenames. However, 'git revert' doesn't take any filenames from the command line, but a single commit. Therefore, it's more sane to offer refs instead. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index bec09bdd62..554a03ff4f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1344,7 +1344,7 @@ _git_revert () return ;; esac - COMPREPLY=() + __gitcomp "$(__git_refs)" } _git_rm () -- cgit v1.2.3 From 7f705dc3686c2b1c77172b6847c1406eb66a20c3 Mon Sep 17 00:00:00 2001 From: Tor Arvid Lund Date: Thu, 4 Dec 2008 14:37:33 +0100 Subject: git-p4: Fix bug in p4Where method. When running: p4 where //depot/SomePath/... The result can in some situations look like: //depot/SomePath/... //client/SomePath/... /home/user/p4root/SomePath/... -//depot/SomePath/UndesiredSubdir/... //client/SomePath/UndesiredSubdir/... /home/user/p4root/SomePath/UndesiredSubdir/... This depends on the users Client view. The current p4Where method will now return /home/user/p4root/SomePath/UndesiredSubdir/... which is not what we want. This patch loops through the results from "p4 where", and picks the one where the depotFile exactly matches the given depotPath (//depot/SomePath/... in this example). Signed-off-by: Tor Arvid Lund Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index b44fbfc9b3..ee504e90ed 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -245,7 +245,15 @@ def p4Cmd(cmd): def p4Where(depotPath): if not depotPath.endswith("/"): depotPath += "/" - output = p4Cmd("where %s..." % depotPath) + depotPath = depotPath + "..." + outputList = p4CmdList("where %s" % depotPath) + output = None + for entry in outputList: + if entry["depotFile"] == depotPath: + output = entry + break + if output == None: + return "" if output["code"] == "error": return "" clientPath = "" -- cgit v1.2.3 From 75bc9573b0e332c34bc1c3d97306077fda573083 Mon Sep 17 00:00:00 2001 From: Tor Arvid Lund Date: Tue, 9 Dec 2008 16:41:50 +0100 Subject: git-p4: Fix regression in p4Where method. Unfortunately, I introduced a bug in commit 7f705dc36 (git-p4: Fix bug in p4Where method). This happens because sometimes the result from "p4 where " doesn't contain a "depotFile" key, but instead a "data" key that needs further parsing. This commit should ensure that both of these cases are checked. Signed-off-by: Tor Arvid Lund Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index ee504e90ed..a85a7b2a58 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -249,9 +249,16 @@ def p4Where(depotPath): outputList = p4CmdList("where %s" % depotPath) output = None for entry in outputList: - if entry["depotFile"] == depotPath: - output = entry - break + if "depotFile" in entry: + if entry["depotFile"] == depotPath: + output = entry + break + elif "data" in entry: + data = entry.get("data") + space = data.find(" ") + if data[:space] == depotPath: + output = entry + break if output == None: return "" if output["code"] == "error": -- cgit v1.2.3 From 025a19298dca1a94e8d39ea28cb57412c12dfbff Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 15 Dec 2008 10:45:48 -0700 Subject: bash completion: Sort config completion variables Sort the config variables to make sync-ing them with Documents/config.txt easier in the future. Signed-off-by: Lee Marlow Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 77 ++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 37 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c79c98ffec..79cbed589d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1206,84 +1206,87 @@ _git_config () esac __gitcomp " apply.whitespace - core.fileMode - core.gitProxy - core.ignoreStat - core.preferSymlinkRefs - core.logAllRefUpdates - core.loosecompression - core.repositoryFormatVersion - core.sharedRepository - core.warnAmbiguousRefs - core.compression - core.packedGitWindowSize - core.packedGitLimit clean.requireForce color.branch color.branch.current color.branch.local - color.branch.remote color.branch.plain + color.branch.remote color.diff - color.diff.plain - color.diff.meta + color.diff.commit color.diff.frag - color.diff.old + color.diff.meta color.diff.new - color.diff.commit + color.diff.old + color.diff.plain color.diff.whitespace color.pager color.status - color.status.header color.status.added color.status.changed + color.status.header color.status.untracked + core.compression + core.fileMode + core.gitProxy + core.ignoreStat + core.logAllRefUpdates + core.loosecompression + core.packedGitLimit + core.packedGitWindowSize + core.preferSymlinkRefs + core.repositoryFormatVersion + core.sharedRepository + core.warnAmbiguousRefs diff.renameLimit diff.renames fetch.unpackLimit format.headers format.subjectprefix - gitcvs.enabled - gitcvs.logfile - gitcvs.allbinary - gitcvs.dbname gitcvs.dbdriver gitcvs.dbuser gitcvs.dbpass - gitcvs.dbtablenameprefix gc.packrefs gc.reflogexpire gc.reflogexpireunreachable gc.rerereresolved gc.rerereunresolved - http.sslVerify - http.sslCert - http.sslKey - http.sslCAInfo - http.sslCAPath - http.maxRequests + gitcvs.allbinary + gitcvs.dbdriver + gitcvs.dbname + gitcvs.dbpass + gitcvs.dbtablenameprefix + gitcvs.dbuser + gitcvs.enabled + gitcvs.logfile http.lowSpeedLimit http.lowSpeedTime + http.maxRequests http.noEPSV + http.sslCAInfo + http.sslCAPath + http.sslCert + http.sslKey + http.sslVerify i18n.commitEncoding i18n.logOutputEncoding log.showroot - merge.tool merge.summary + merge.tool merge.verbosity - pack.window - pack.depth - pack.windowMemory pack.compression - pack.deltaCacheSize pack.deltaCacheLimit + pack.deltaCacheSize + pack.depth + pack.window + pack.windowMemory pull.octopus pull.twohead + receive.denyNonFastForwards + receive.unpackLimit repack.useDeltaBaseOffset showbranch.default tar.umask transfer.unpackLimit - receive.unpackLimit - receive.denyNonFastForwards - user.name user.email + user.name user.signingkey branch. remote. " -- cgit v1.2.3 From 98171a07ae74e796a6c78e7c446ac9a5aaf28c07 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Mon, 15 Dec 2008 10:45:49 -0700 Subject: bash completion: Sync config variables with their man pages Add 'normal' to config color options. Add 'mergeoptions' to branch config options. Add 'proxy' and 'mirror' to remote config options. Signed-off-by: Lee Marlow Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 87 +++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 79cbed589d..e00454983e 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1155,7 +1155,7 @@ _git_config () ;; color.*.*) __gitcomp " - black red green yellow blue magenta cyan white + normal black red green yellow blue magenta cyan white bold dim ul blink reverse " return @@ -1179,7 +1179,7 @@ _git_config () branch.*.*) local pfx="${cur%.*}." cur="${cur##*.}" - __gitcomp "remote merge" "$pfx" "$cur" + __gitcomp "remote merge mergeoptions" "$pfx" "$cur" return ;; branch.*) @@ -1192,7 +1192,7 @@ _git_config () local pfx="${cur%.*}." cur="${cur##*.}" __gitcomp " - url fetch push skipDefaultUpdate + url proxy fetch push mirror skipDefaultUpdate receivepack uploadpack tagopt " "$pfx" "$cur" return @@ -1206,6 +1206,8 @@ _git_config () esac __gitcomp " apply.whitespace + branch.autosetupmerge + branch.autosetuprebase clean.requireForce color.branch color.branch.current @@ -1220,46 +1222,95 @@ _git_config () color.diff.old color.diff.plain color.diff.whitespace + color.interactive + color.interactive.header + color.interactive.help + color.interactive.prompt color.pager color.status color.status.added color.status.changed color.status.header + color.status.nobranch color.status.untracked + color.status.updated + color.ui + commit.template + core.autocrlf + core.bare core.compression + core.deltaBaseCacheLimit + core.editor + core.excludesfile core.fileMode + core.fsyncobjectfiles core.gitProxy + core.ignoreCygwinFSTricks core.ignoreStat core.logAllRefUpdates core.loosecompression core.packedGitLimit core.packedGitWindowSize + core.pager core.preferSymlinkRefs + core.preloadindex + core.quotepath core.repositoryFormatVersion + core.safecrlf core.sharedRepository + core.symlinks + core.trustctime core.warnAmbiguousRefs + core.whitespace + core.worktree + diff.autorefreshindex + diff.external + diff.mnemonicprefix diff.renameLimit + diff.renameLimit. diff.renames fetch.unpackLimit format.headers - format.subjectprefix + format.numbered + format.pretty + format.suffix + gc.aggressiveWindow + gc.auto + gc.autopacklimit gc.packrefs + gc.pruneexpire gc.reflogexpire gc.reflogexpireunreachable gc.rerereresolved gc.rerereunresolved gitcvs.allbinary + gitcvs.dbTableNamePrefix gitcvs.dbdriver gitcvs.dbname gitcvs.dbpass - gitcvs.dbtablenameprefix gitcvs.dbuser gitcvs.enabled gitcvs.logfile + gitcvs.usecrlfattr + gui.blamehistoryctx + gui.commitmsgwidth + gui.copyblamethreshold + gui.diffcontext + gui.encoding + gui.fastcopyblame + gui.matchtrackingbranch + gui.newbranchtemplate + gui.pruneduringfetch + gui.spellingdictionary + gui.trustmtime + help.autocorrect + help.browser + help.format http.lowSpeedLimit http.lowSpeedTime http.maxRequests http.noEPSV + http.proxy http.sslCAInfo http.sslCAPath http.sslCert @@ -1267,27 +1318,49 @@ _git_config () http.sslVerify i18n.commitEncoding i18n.logOutputEncoding + instaweb.browser + instaweb.httpd + instaweb.local + instaweb.modulepath + instaweb.port + log.date log.showroot - merge.summary + man.viewer + merge.conflictstyle + merge.log + merge.renameLimit + merge.stat merge.tool merge.verbosity + mergetool.keepBackup pack.compression pack.deltaCacheLimit pack.deltaCacheSize pack.depth + pack.indexVersion + pack.packSizeLimit + pack.threads pack.window pack.windowMemory pull.octopus pull.twohead + receive.denyCurrentBranch + receive.denyDeletes receive.denyNonFastForwards + receive.fsckObjects receive.unpackLimit - repack.useDeltaBaseOffset + repack.usedeltabaseoffset + rerere.autoupdate + rerere.enabled showbranch.default + status.relativePaths + status.showUntrackedFiles tar.umask transfer.unpackLimit user.email user.name user.signingkey + web.browser branch. remote. " } -- cgit v1.2.3 From f66bc5f928194366ee5eb78ef18a3562fb1bb7cf Mon Sep 17 00:00:00 2001 From: Richard Hartmann Date: Mon, 22 Dec 2008 00:17:32 +0100 Subject: Always show which directory is not a git repository Unify all fatal: Not a git repository error messages so they include path information. Signed-off-by: Richard Hartmann Signed-off-by: Junio C Hamano --- contrib/workdir/git-new-workdir | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir index 7959eab902..993cacf324 100755 --- a/contrib/workdir/git-new-workdir +++ b/contrib/workdir/git-new-workdir @@ -22,7 +22,7 @@ branch=$3 # want to make sure that what is pointed to has a .git directory ... git_dir=$(cd "$orig_git" 2>/dev/null && git rev-parse --git-dir 2>/dev/null) || - die "\"$orig_git\" is not a git repository!" + die "Not a git repository: \"$orig_git\"" case "$git_dir" in .git) -- cgit v1.2.3 From 6d0e674a575421347abe5749e645ca6dc78c8207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= Date: Sun, 28 Dec 2008 19:45:32 +0100 Subject: diff: add option to show context between close hunks Merge two hunks if there is only the specified number of otherwise unshown context between them. For --inter-hunk-context=1, the resulting patch has the same number of lines but shows uninterrupted context instead of a context header line in between. Patches generated with this option are easier to read but are also more likely to conflict if the file to be patched contains other changes. This patch keeps the default for this option at 0. It is intended to just make the feature available in order to see its advantages and downsides. Signed-off-by: Rene Scharfe Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e00454983e..a046441974 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -776,6 +776,7 @@ _git_diff () --no-ext-diff --no-prefix --src-prefix= --dst-prefix= --base --ours --theirs + --inter-hunk-context= " return ;; @@ -967,6 +968,7 @@ _git_log () --color-words --walk-reflogs --parents --children --full-history --merge + --inter-hunk-context= " return ;; -- cgit v1.2.3 From e89e2ed7c225cf16cffbd9648895528e471e2fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 29 Dec 2008 16:05:46 +0100 Subject: bash: add '--merge' to 'git reset' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e00454983e..3b25d48098 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1403,7 +1403,7 @@ _git_reset () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--mixed --hard --soft" + __gitcomp "--merge --mixed --hard --soft" return ;; esac -- cgit v1.2.3 From cc545709253fe8440db2648cb5c771e5b126bdb5 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Thu, 1 Jan 2009 17:39:37 +0100 Subject: bash completions: Add the --patience option Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e00454983e..b98d765deb 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -776,6 +776,7 @@ _git_diff () --no-ext-diff --no-prefix --src-prefix= --dst-prefix= --base --ours --theirs + --patience " return ;; @@ -967,6 +968,7 @@ _git_log () --color-words --walk-reflogs --parents --children --full-history --merge + --patience " return ;; -- cgit v1.2.3 From c9a114b591e42be3ed8e5e4812dfd1031df79a78 Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 10 Dec 2008 12:39:17 -0700 Subject: bash completion: Add '--intent-to-add' long option for 'git add' Signed-off-by: Lee Marlow Trivially-Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8ec782dc54..e986e783e2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -563,7 +563,7 @@ _git_add () --*) __gitcomp " --interactive --refresh --patch --update --dry-run - --ignore-errors + --ignore-errors --intent-to-add " return esac -- cgit v1.2.3 From df3987717f1546719a1bf1828fb3714cd5ca9faa Mon Sep 17 00:00:00 2001 From: Lee Marlow Date: Wed, 10 Dec 2008 12:39:18 -0700 Subject: bash completion: Use 'git add' completions for 'git stage' Signed-off-by: Lee Marlow Trivially-Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e986e783e2..7b074d7985 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1738,6 +1738,7 @@ _git () show) _git_show ;; show-branch) _git_show_branch ;; stash) _git_stash ;; + stage) _git_add ;; submodule) _git_submodule ;; svn) _git_svn ;; tag) _git_tag ;; -- cgit v1.2.3 From 47a845bfc3a9e1d378c43c3b3ea4291d5d79eca8 Mon Sep 17 00:00:00 2001 From: "jidanni@jidanni.org" Date: Tue, 13 Jan 2009 09:19:42 +0800 Subject: contrib/examples/README: give an explanation of the status of these files We attempt to give an explanation of the status of the files in this directory. Signed-off-by: jidanni Signed-off-by: Junio C Hamano --- contrib/examples/README | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 contrib/examples/README (limited to 'contrib') diff --git a/contrib/examples/README b/contrib/examples/README new file mode 100644 index 0000000000..6946f3dd2a --- /dev/null +++ b/contrib/examples/README @@ -0,0 +1,3 @@ +These are original scripted implementations, kept primarily for their +reference value to any aspiring plumbing users who want to learn how +pieces can be fit together. -- cgit v1.2.3 From abc776f7880c2dc0a4179420366e40ecb99d223f Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Tue, 13 Jan 2009 03:10:26 +0100 Subject: contrib/vim: change URL to point to the latest syntax files Vim's SVN repository doesn't offer the latest runtime files, since normally they are only updated there on a release. Though currently there is no difference between the SVN and HTTP/FTP version of the git syntax files. Signed-off-by: Markus Heidelberg Acked-by: Jeff King Signed-off-by: Junio C Hamano --- contrib/vim/README | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/vim/README b/contrib/vim/README index c487346eba..fca1e17251 100644 --- a/contrib/vim/README +++ b/contrib/vim/README @@ -5,11 +5,13 @@ automatically. If you have an older version of vim, you can get the latest syntax files from the vim project: - http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/git.vim - http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitcommit.vim - http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitconfig.vim - http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitrebase.vim - http://vim.svn.sourceforge.net/viewvc/vim/trunk/runtime/syntax/gitsendemail.vim + http://ftp.vim.org/pub/vim/runtime/syntax/git.vim + http://ftp.vim.org/pub/vim/runtime/syntax/gitcommit.vim + http://ftp.vim.org/pub/vim/runtime/syntax/gitconfig.vim + http://ftp.vim.org/pub/vim/runtime/syntax/gitrebase.vim + http://ftp.vim.org/pub/vim/runtime/syntax/gitsendemail.vim + +These files are also available via FTP at the same location. To install: -- cgit v1.2.3 From 25a31f814016891c7728fdebca056ef47ca75547 Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Thu, 15 Jan 2009 11:02:21 -0500 Subject: bash-completion: Support running when set -u is enabled Under "set -u" semantics, it is an error to access undefined variables. Some user environments may enable this setting in the interactive shell. In any context where the completion functions access an undefined variable, accessing a default empty string (aka "${1-}" instead of "$1") is a reasonable way to code the function, as it silences the undefined variable error while still supplying an empty string. In this patch, functions that should always take an argument still use $1. Functions that have optional arguments use ${1-}. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 7b074d7985..5d1515cec4 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -52,7 +52,7 @@ esac __gitdir () { - if [ -z "$1" ]; then + if [ -z "${1-}" ]; then if [ -n "$__git_dir" ]; then echo "$__git_dir" elif [ -d .git ]; then @@ -111,7 +111,7 @@ __git_ps1 () fi fi - if [ -n "$1" ]; then + if [ -n "${1-}" ]; then printf "$1" "${b##refs/heads/}$r" else printf " (%s)" "${b##refs/heads/}$r" @@ -143,8 +143,8 @@ __gitcomp () ;; *) local IFS=$'\n' - COMPREPLY=($(compgen -P "$2" \ - -W "$(__gitcomp_1 "$1" "$4")" \ + COMPREPLY=($(compgen -P "${2-}" \ + -W "$(__gitcomp_1 "${1-}" "${4-}")" \ -- "$cur")) ;; esac @@ -152,13 +152,13 @@ __gitcomp () __git_heads () { - local cmd i is_hash=y dir="$(__gitdir "$1")" + local cmd i is_hash=y dir="$(__gitdir "${1-}")" if [ -d "$dir" ]; then git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ refs/heads return fi - for i in $(git ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "${1-}" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -170,13 +170,13 @@ __git_heads () __git_tags () { - local cmd i is_hash=y dir="$(__gitdir "$1")" + local cmd i is_hash=y dir="$(__gitdir "${1-}")" if [ -d "$dir" ]; then git --git-dir="$dir" for-each-ref --format='%(refname:short)' \ refs/tags return fi - for i in $(git ls-remote "$1" 2>/dev/null); do + for i in $(git ls-remote "${1-}" 2>/dev/null); do case "$is_hash,$i" in y,*) is_hash=n ;; n,*^{}) is_hash=y ;; @@ -188,7 +188,7 @@ __git_tags () __git_refs () { - local i is_hash=y dir="$(__gitdir "$1")" + local i is_hash=y dir="$(__gitdir "${1-}")" local cur="${COMP_WORDS[COMP_CWORD]}" format refs if [ -d "$dir" ]; then case "$cur" in -- cgit v1.2.3 From 50e126e185c196b9b66dcdefb4b05f090d62dd4c Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Thu, 15 Jan 2009 11:02:22 -0500 Subject: bash-completion: Try bash completions before simple filetype When a git completion is not found, a bash shell should try bash-type completions first before going to standard filetype completions. This patch adds "-o bashdefault" to the completion line. If that option is not available, it uses the old method. This behavior was inspired by Mercurial's bash completion script. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 5d1515cec4..201f9a6894 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1766,13 +1766,16 @@ _gitk () __git_complete_revlist } -complete -o default -o nospace -F _git git -complete -o default -o nospace -F _gitk gitk +complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \ + || complete -o default -o nospace -F _git git +complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \ + || complete -o default -o nospace -F _gitk gitk # The following are necessary only for Cygwin, and only are needed # when the user has tab-completed the executable name and consequently # included the '.exe' suffix. # if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then -complete -o default -o nospace -F _git git.exe +complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \ + || complete -o default -o nospace -F _git git.exe fi -- cgit v1.2.3 From a42577d4c868141343378824814c058de043e24d Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Thu, 15 Jan 2009 11:02:23 -0500 Subject: bash-completion: Add comments to remind about required arguments Add a few simple comments above commands that take arguments. These comments are meant to remind developers of potential problems that can occur when the script is sourced on systems with "set -u." Any function which requires arguments really ought to be called with explicit arguments given. Also adds a #!bash to the top of bash completions so that editing software can always identify that the file is of sh type. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 201f9a6894..f8b845a4ac 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1,3 +1,4 @@ +#!bash # # bash completion support for core Git. # @@ -50,6 +51,8 @@ case "$COMP_WORDBREAKS" in *) COMP_WORDBREAKS="$COMP_WORDBREAKS:" esac +# __gitdir accepts 0 or 1 arguments (i.e., location) +# returns location of .git repo __gitdir () { if [ -z "${1-}" ]; then @@ -67,6 +70,8 @@ __gitdir () fi } +# __git_ps1 accepts 0 or 1 arguments (i.e., format string) +# returns text to add to bash PS1 prompt (includes branch name) __git_ps1 () { local g="$(git rev-parse --git-dir 2>/dev/null)" @@ -119,6 +124,7 @@ __git_ps1 () fi } +# __gitcomp_1 requires 2 arguments __gitcomp_1 () { local c IFS=' '$'\t'$'\n' @@ -131,6 +137,8 @@ __gitcomp_1 () done } +# __gitcomp accepts 1, 2, 3, or 4 arguments +# generates completion reply with compgen __gitcomp () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -150,6 +158,7 @@ __gitcomp () esac } +# __git_heads accepts 0 or 1 arguments (to pass to __gitdir) __git_heads () { local cmd i is_hash=y dir="$(__gitdir "${1-}")" @@ -168,6 +177,7 @@ __git_heads () done } +# __git_tags accepts 0 or 1 arguments (to pass to __gitdir) __git_tags () { local cmd i is_hash=y dir="$(__gitdir "${1-}")" @@ -186,6 +196,7 @@ __git_tags () done } +# __git_refs accepts 0 or 1 arguments (to pass to __gitdir) __git_refs () { local i is_hash=y dir="$(__gitdir "${1-}")" @@ -218,6 +229,7 @@ __git_refs () done } +# __git_refs2 requires 1 argument (to pass to __git_refs) __git_refs2 () { local i @@ -226,6 +238,7 @@ __git_refs2 () done } +# __git_refs_remotes requires 1 argument (to pass to ls-remote) __git_refs_remotes () { local cmd i is_hash=y @@ -470,6 +483,7 @@ __git_aliases () done } +# __git_aliased_command requires 1 argument __git_aliased_command () { local word cmdline=$(git --git-dir="$(__gitdir)" \ @@ -482,6 +496,7 @@ __git_aliased_command () done } +# __git_find_subcommand requires 1 argument __git_find_subcommand () { local word subcommand c=1 -- cgit v1.2.3 From 7de931c3c2c7850dd7f26c24fbe9d12d223f55f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 16 Jan 2009 17:01:57 +0100 Subject: bash: remove unnecessary checks for long options with argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __gitcomp takes care of it since 5447aac7 (bash: fix long option with argument double completion, 2008-03-05) Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 -- 1 file changed, 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f8b845a4ac..9021220421 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -643,7 +643,6 @@ _git_branch () done case "${COMP_WORDS[COMP_CWORD]}" in - --*=*) COMPREPLY=() ;; --*) __gitcomp " --color --no-color --verbose --abbrev= --no-abbrev @@ -1689,7 +1688,6 @@ _git () if [ -z "$command" ]; then case "${COMP_WORDS[COMP_CWORD]}" in - --*=*) COMPREPLY=() ;; --*) __gitcomp " --paginate --no-pager -- cgit v1.2.3 From 8108513422eac0b0df947ab58f63a6a215faa1ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 16 Jan 2009 17:02:04 +0100 Subject: bash: add missing format-patch command line options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 ++ 1 file changed, 2 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9021220421..80edfcacc9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -838,6 +838,8 @@ _git_format_patch () --not --all --cover-letter --no-prefix --src-prefix= --dst-prefix= + --inline --suffix= --ignore-if-in-upstream + --subject-prefix= " return ;; -- cgit v1.2.3 From 3d279863dedf6c07eefe307b27fb3a08e519140f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 16 Jan 2009 17:02:15 +0100 Subject: bash: refactor 'git log --pretty=' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both 'git log' and 'show' have the same '--pretty=' option with the same formats. So refactor these formats into a common variable. While at it, also add 'format:' to the list. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 80edfcacc9..ec701e8069 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -947,6 +947,8 @@ _git_ls_tree () __git_complete_file } +__git_log_pretty_formats="oneline short medium full fuller email raw format:" + _git_log () { __git_has_doubledash && return @@ -954,8 +956,7 @@ _git_log () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --pretty=*) - __gitcomp " - oneline short medium full fuller email raw + __gitcomp "$__git_log_pretty_formats " "" "${cur##--pretty=}" return ;; @@ -1483,8 +1484,7 @@ _git_show () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --pretty=*) - __gitcomp " - oneline short medium full fuller email raw + __gitcomp "$__git_log_pretty_formats " "" "${cur##--pretty=}" return ;; -- cgit v1.2.3 From 5c38ea31f345d08f37685cf4f50c599a7af56bcf Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Fri, 16 Jan 2009 00:00:02 -0800 Subject: contrib: add 'git difftool' for launching common merge tools 'git difftool' is a git command that allows you to compare and edit files between revisions using common merge tools. 'git difftool' does what 'git mergetool' does but its use is for non-merge situations such as when preparing commits or comparing changes against the index. It uses the same configuration variables as 'git mergetool' and provides the same command-line interface as 'git diff'. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool | 74 +++++++++++ contrib/difftool/git-difftool-helper | 240 +++++++++++++++++++++++++++++++++++ contrib/difftool/git-difftool.txt | 104 +++++++++++++++ 3 files changed, 418 insertions(+) create mode 100755 contrib/difftool/git-difftool create mode 100755 contrib/difftool/git-difftool-helper create mode 100644 contrib/difftool/git-difftool.txt (limited to 'contrib') diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool new file mode 100755 index 0000000000..1fc087c5fc --- /dev/null +++ b/contrib/difftool/git-difftool @@ -0,0 +1,74 @@ +#!/usr/bin/env perl +# Copyright (c) 2009 David Aguilar +# +# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible +# git-difftool-helper script. This script exports +# GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and +# GIT_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper. +# Any arguments that are unknown to this script are forwarded to 'git diff'. + +use strict; +use warnings; +use Cwd qw(abs_path); +use File::Basename qw(dirname); + +my $DIR = abs_path(dirname($0)); + + +sub usage +{ + print << 'USAGE'; + +usage: git difftool [--no-prompt] [--tool=tool] ["git diff" options] +USAGE + exit 1; +} + +sub setup_environment +{ + $ENV{PATH} = "$DIR:$ENV{PATH}"; + $ENV{GIT_PAGER} = ''; + $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper'; +} + +sub exe +{ + my $exe = shift; + return defined $ENV{COMSPEC} ? "$exe.exe" : $exe; +} + +sub generate_command +{ + my @command = (exe('git'), 'diff'); + my $skip_next = 0; + my $idx = -1; + for my $arg (@ARGV) { + $idx++; + if ($skip_next) { + $skip_next = 0; + next; + } + if ($arg eq '-t' or $arg eq '--tool') { + usage() if $#ARGV <= $idx; + $ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1]; + $skip_next = 1; + next; + } + if ($arg =~ /^--tool=/) { + $ENV{GIT_MERGE_TOOL} = substr($arg, 7); + next; + } + if ($arg eq '--no-prompt') { + $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true'; + next; + } + if ($arg eq '-h' or $arg eq '--help') { + usage(); + } + push @command, $arg; + } + return @command +} + +setup_environment(); +exec(generate_command()); diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper new file mode 100755 index 0000000000..0b266e3603 --- /dev/null +++ b/contrib/difftool/git-difftool-helper @@ -0,0 +1,240 @@ +#!/bin/sh +# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher. +# It supports kdiff3, tkdiff, xxdiff, meld, opendiff, emerge, ecmerge, +# vimdiff, gvimdiff, and custom user-configurable tools. +# This script is typically launched by using the 'git difftool' +# convenience command. +# +# Copyright (c) 2009 David Aguilar + +# Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt. +should_prompt () { + ! test -n "$GIT_DIFFTOOL_NO_PROMPT" +} + +# Should we keep the backup .orig file? +keep_backup_mode="$(git config --bool merge.keepBackup || echo true)" +keep_backup () { + test "$keep_backup_mode" = "true" +} + +# This function manages the backup .orig file. +# A backup $MERGED.orig file is created if changes are detected. +cleanup_temp_files () { + if test -n "$MERGED"; then + if keep_backup && test "$MERGED" -nt "$BACKUP"; then + test -f "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig" + else + rm -f -- "$BACKUP" + fi + fi +} + +# This is called when users Ctrl-C out of git-difftool-helper +sigint_handler () { + echo + cleanup_temp_files + exit 1 +} + +# This function prepares temporary files and launches the appropriate +# merge tool. +launch_merge_tool () { + # Merged is the filename as it appears in the work tree + # Local is the contents of a/filename + # Remote is the contents of b/filename + # Custom merge tool commands might use $BASE so we provide it + MERGED="$1" + LOCAL="$2" + REMOTE="$3" + BASE="$1" + ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')" + BACKUP="$MERGED.BACKUP.$ext" + + # Create and ensure that we clean up $BACKUP + test -f "$MERGED" && cp -- "$MERGED" "$BACKUP" + trap sigint_handler SIGINT + + # $LOCAL and $REMOTE are temporary files so prompt + # the user with the real $MERGED name before launching $merge_tool. + if should_prompt; then + printf "\nViewing: '$MERGED'\n" + printf "Hit return to launch '%s': " "$merge_tool" + read ans + fi + + # Run the appropriate merge tool command + case "$merge_tool" in + kdiff3) + basename=$(basename "$MERGED") + "$merge_tool_path" --auto \ + --L1 "$basename (A)" \ + --L2 "$basename (B)" \ + -o "$MERGED" "$LOCAL" "$REMOTE" \ + > /dev/null 2>&1 + ;; + + tkdiff) + "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE" + ;; + + meld|vimdiff) + "$merge_tool_path" "$LOCAL" "$REMOTE" + ;; + + gvimdiff) + "$merge_tool_path" -f "$LOCAL" "$REMOTE" + ;; + + xxdiff) + "$merge_tool_path" \ + -X \ + -R 'Accel.SaveAsMerged: "Ctrl-S"' \ + -R 'Accel.Search: "Ctrl+F"' \ + -R 'Accel.SearchForward: "Ctrl-G"' \ + --merged-file "$MERGED" \ + "$LOCAL" "$REMOTE" + ;; + + opendiff) + "$merge_tool_path" "$LOCAL" "$REMOTE" \ + -merge "$MERGED" | cat + ;; + + ecmerge) + "$merge_tool_path" "$LOCAL" "$REMOTE" \ + --default --mode=merge2 --to="$MERGED" + ;; + + emerge) + "$merge_tool_path" -f emerge-files-command \ + "$LOCAL" "$REMOTE" "$(basename "$MERGED")" + ;; + + *) + if test -n "$merge_tool_cmd"; then + ( eval $merge_tool_cmd ) + fi + ;; + esac + + cleanup_temp_files +} + +# Verifies that mergetool..cmd exists +valid_custom_tool() { + merge_tool_cmd="$(git config mergetool.$1.cmd)" + test -n "$merge_tool_cmd" +} + +# Verifies that the chosen merge tool is properly setup. +# Built-in merge tools are always valid. +valid_tool() { + case "$1" in + kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) + ;; # happy + *) + if ! valid_custom_tool "$1" + then + return 1 + fi + ;; + esac +} + +# Sets up the merge_tool_path variable. +# This handles the mergetool..path configuration. +init_merge_tool_path() { + merge_tool_path=$(git config mergetool."$1".path) + if test -z "$merge_tool_path"; then + case "$1" in + emerge) + merge_tool_path=emacs + ;; + *) + merge_tool_path="$1" + ;; + esac + fi +} + +# Allow the GIT_MERGE_TOOL variable to provide a default value +test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL" + +# If not merge tool was specified then use the merge.tool +# configuration variable. If that's invalid then reset merge_tool. +if test -z "$merge_tool"; then + merge_tool=$(git config merge.tool) + if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then + echo >&2 "git config option merge.tool set to unknown tool: $merge_tool" + echo >&2 "Resetting to default..." + unset merge_tool + fi +fi + +# Try to guess an appropriate merge tool if no tool has been set. +if test -z "$merge_tool"; then + + # We have a $DISPLAY so try some common UNIX merge tools + if test -n "$DISPLAY"; then + merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff" + # If gnome then prefer meld + if test -n "$GNOME_DESKTOP_SESSION_ID"; then + merge_tool_candidates="meld $merge_tool_candidates" + fi + # If KDE then prefer kdiff3 + if test "$KDE_FULL_SESSION" = "true"; then + merge_tool_candidates="kdiff3 $merge_tool_candidates" + fi + fi + + # $EDITOR is emacs so add emerge as a candidate + if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then + merge_tool_candidates="$merge_tool_candidates emerge" + fi + + # $EDITOR is vim so add vimdiff as a candidate + if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then + merge_tool_candidates="$merge_tool_candidates vimdiff" + fi + + merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" + echo "merge tool candidates: $merge_tool_candidates" + + # Loop over each candidate and stop when a valid merge tool is found. + for i in $merge_tool_candidates + do + init_merge_tool_path $i + if type "$merge_tool_path" > /dev/null 2>&1; then + merge_tool=$i + break + fi + done + + if test -z "$merge_tool" ; then + echo "No known merge resolution program available." + exit 1 + fi + +else + # A merge tool has been set, so verify that it's valid. + if ! valid_tool "$merge_tool"; then + echo >&2 "Unknown merge tool $merge_tool" + exit 1 + fi + + init_merge_tool_path "$merge_tool" + + if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then + echo "The merge tool $merge_tool is not available as '$merge_tool_path'" + exit 1 + fi +fi + + +# Launch the merge tool on each path provided by 'git diff' +while test $# -gt 6 +do + launch_merge_tool "$1" "$2" "$5" + shift 7 +done diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt new file mode 100644 index 0000000000..3940c7057c --- /dev/null +++ b/contrib/difftool/git-difftool.txt @@ -0,0 +1,104 @@ +git-difftool(1) +=============== + +NAME +---- +git-difftool - compare changes using common merge tools + +SYNOPSIS +-------- +'git difftool' [--tool=] [--no-prompt] ['git diff' options] + +DESCRIPTION +----------- +'git difftool' is a git command that allows you to compare and edit files +between revisions using common merge tools. At its most basic level, +'git difftool' does what 'git mergetool' does but its use is for non-merge +situations such as when preparing commits or comparing changes against +the index. + +'git difftool' is a frontend to 'git diff' and accepts the same +arguments and options. + +See linkgit:git-diff[7] for the full list of supported options. + +OPTIONS +------- +-t :: +--tool=:: + Use the merge resolution program specified by . + Valid merge tools are: + kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff ++ +If a merge resolution program is not specified, 'git difftool' +will use the configuration variable `merge.tool`. If the +configuration variable `merge.tool` is not set, 'git difftool' +will pick a suitable default. ++ +You can explicitly provide a full path to the tool by setting the +configuration variable `mergetool..path`. For example, you +can configure the absolute path to kdiff3 by setting +`mergetool.kdiff3.path`. Otherwise, 'git difftool' assumes the +tool is available in PATH. ++ +Instead of running one of the known merge tool programs, +'git difftool' can be customized to run an alternative program +by specifying the command line to invoke in a configuration +variable `mergetool..cmd`. ++ +When 'git difftool' is invoked with this tool (either through the +`-t` or `--tool` option or the `merge.tool` configuration variable) +the configured command line will be invoked with the following +variables available: `$LOCAL` is set to the name of the temporary +file containing the contents of the diff pre-image and `$REMOTE` +is set to the name of the temporary file containing the contents +of the diff post-image. `$BASE` is provided for compatibility +with custom merge tool commands and has the same value as `$LOCAL`. + +--no-prompt:: + Do not prompt before launching a merge tool. + +CONFIG VARIABLES +---------------- +merge.tool:: + The default merge tool to use. ++ +See the `--tool=` option above for more details. + +merge.keepBackup:: + The original, unedited file content can be saved to a file with + a `.orig` extension. Defaults to `true` (i.e. keep the backup files). + +mergetool..path:: + Override the path for the given tool. This is useful in case + your tool is not in the PATH. + +mergetool..cmd:: + Specify the command to invoke the specified merge tool. ++ +See the `--tool=` option above for more details. + + +SEE ALSO +-------- +linkgit:git-diff[7]:: + Show changes between commits, commit and working tree, etc + +linkgit:git-mergetool[1]:: + Run merge conflict resolution tools to resolve merge conflicts + +linkgit:git-config[7]:: + Get and set repository or global options + + +AUTHOR +------ +Written by David Aguilar . + +Documentation +-------------- +Documentation by David Aguilar and the git-list . + +GIT +--- +Part of the linkgit:git[1] suite -- cgit v1.2.3 From e82f0d73f02e89a95d9477911774d314f70f1063 Mon Sep 17 00:00:00 2001 From: Pete Harlan Date: Sat, 17 Jan 2009 20:10:14 -0800 Subject: git-svn: Add --localtime option to "fetch" By default git-svn stores timestamps of fetched commits in Subversion's UTC format. Passing --localtime to fetch will convert them to the timezone of the server on which git-svn is run. This makes the timestamps of a resulting "git log" agree with what "svn log" shows for the same repository. Signed-off-by: Pete Harlan Acked-by: Eric Wong --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index ec701e8069..60497a4c2a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1576,7 +1576,7 @@ _git_svn () --follow-parent --authors-file= --repack= --no-metadata --use-svm-props --use-svnsync-props --log-window-size= --no-checkout --quiet - --repack-flags --user-log-author $remote_opts + --repack-flags --user-log-author --localtime $remote_opts " local init_opts=" --template= --shared= --trunk= --tags= -- cgit v1.2.3 From 507cfcbd81196e14053bcd25735aaefabd99395d Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sun, 18 Jan 2009 21:27:19 -0800 Subject: difftool: fix documentation problems This patch makes the difftool docs always refer to the git-difftool script using the dashed form of the name. Only command examples use the non-dashed form now. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool | 5 ++--- contrib/difftool/git-difftool.txt | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool index 1fc087c5fc..0cda3d2eea 100755 --- a/contrib/difftool/git-difftool +++ b/contrib/difftool/git-difftool @@ -4,7 +4,7 @@ # This is a wrapper around the GIT_EXTERNAL_DIFF-compatible # git-difftool-helper script. This script exports # GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and -# GIT_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper. +# GIT_DIFFTOOL_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper. # Any arguments that are unknown to this script are forwarded to 'git diff'. use strict; @@ -18,8 +18,7 @@ my $DIR = abs_path(dirname($0)); sub usage { print << 'USAGE'; - -usage: git difftool [--no-prompt] [--tool=tool] ["git diff" options] +usage: git difftool [--tool=] [--no-prompt] ["git diff" options] USAGE exit 1; } diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index 3940c7057c..ca3dbd2465 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -11,16 +11,16 @@ SYNOPSIS DESCRIPTION ----------- -'git difftool' is a git command that allows you to compare and edit files +'git-difftool' is a git command that allows you to compare and edit files between revisions using common merge tools. At its most basic level, -'git difftool' does what 'git mergetool' does but its use is for non-merge +'git-difftool' does what 'git-mergetool' does but its use is for non-merge situations such as when preparing commits or comparing changes against the index. 'git difftool' is a frontend to 'git diff' and accepts the same arguments and options. -See linkgit:git-diff[7] for the full list of supported options. +See linkgit:git-diff[1] for the full list of supported options. OPTIONS ------- @@ -30,7 +30,7 @@ OPTIONS Valid merge tools are: kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff + -If a merge resolution program is not specified, 'git difftool' +If a merge resolution program is not specified, 'git-difftool' will use the configuration variable `merge.tool`. If the configuration variable `merge.tool` is not set, 'git difftool' will pick a suitable default. @@ -38,15 +38,15 @@ will pick a suitable default. You can explicitly provide a full path to the tool by setting the configuration variable `mergetool..path`. For example, you can configure the absolute path to kdiff3 by setting -`mergetool.kdiff3.path`. Otherwise, 'git difftool' assumes the +`mergetool.kdiff3.path`. Otherwise, 'git-difftool' assumes the tool is available in PATH. + Instead of running one of the known merge tool programs, -'git difftool' can be customized to run an alternative program +'git-difftool' can be customized to run an alternative program by specifying the command line to invoke in a configuration variable `mergetool..cmd`. + -When 'git difftool' is invoked with this tool (either through the +When 'git-difftool' is invoked with this tool (either through the `-t` or `--tool` option or the `merge.tool` configuration variable) the configured command line will be invoked with the following variables available: `$LOCAL` is set to the name of the temporary @@ -56,7 +56,7 @@ of the diff post-image. `$BASE` is provided for compatibility with custom merge tool commands and has the same value as `$LOCAL`. --no-prompt:: - Do not prompt before launching a merge tool. + Do not prompt before launching a diff tool. CONFIG VARIABLES ---------------- @@ -81,13 +81,13 @@ See the `--tool=` option above for more details. SEE ALSO -------- -linkgit:git-diff[7]:: +linkgit:git-diff[1]:: Show changes between commits, commit and working tree, etc linkgit:git-mergetool[1]:: Run merge conflict resolution tools to resolve merge conflicts -linkgit:git-config[7]:: +linkgit:git-config[1]:: Get and set repository or global options -- cgit v1.2.3 From 28da86a58d7861626eb9d33a1bcfa3e1e79a4d13 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sun, 18 Jan 2009 21:34:29 -0800 Subject: difftool: put the cursor on the editable file for Vim You only need to edit worktree files when comparing against the worktree. Put the cursor automatically into its window for vimdiff and gvimdiff to avoid doing l every time. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index 0b266e3603..f013726d0f 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -78,12 +78,16 @@ launch_merge_tool () { "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE" ;; - meld|vimdiff) + meld) "$merge_tool_path" "$LOCAL" "$REMOTE" ;; + vimdiff) + "$merge_tool_path" -c "wincmd l" "$LOCAL" "$REMOTE" + ;; + gvimdiff) - "$merge_tool_path" -f "$LOCAL" "$REMOTE" + "$merge_tool_path" -c "wincmd l" -f "$LOCAL" "$REMOTE" ;; xxdiff) -- cgit v1.2.3 From 47d5a8fa7188cceb90fe50f1561e64381e8530a3 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Mon, 19 Jan 2009 22:17:59 +0100 Subject: bash completion: move pickaxe options to log Move the options --pickaxe-all and --pickaxe-regex to git-log, where they make more sense than with git-diff. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 60497a4c2a..b5d3bbbceb 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -784,7 +784,7 @@ _git_diff () --patch-with-stat --name-only --name-status --color --no-color --color-words --no-renames --check --full-index --binary --abbrev --diff-filter= - --find-copies-harder --pickaxe-all --pickaxe-regex + --find-copies-harder --text --ignore-space-at-eol --ignore-space-change --ignore-all-space --exit-code --quiet --ext-diff --no-ext-diff @@ -986,6 +986,7 @@ _git_log () --parents --children --full-history --merge --inter-hunk-context= + --pickaxe-all --pickaxe-regex " return ;; -- cgit v1.2.3 From 20bf7292314972d4c418056ad94c28c6363058d7 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Mon, 19 Jan 2009 22:18:00 +0100 Subject: bash completion: refactor diff options diff, log and show all take the same diff options. Refactor them from __git_diff and __git_log into a variable, and complete them in __git_show too. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 36 ++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 15 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b5d3bbbceb..a1298c4f96 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -773,14 +773,7 @@ _git_describe () __gitcomp "$(__git_refs)" } -_git_diff () -{ - __git_has_doubledash && return - - local cur="${COMP_WORDS[COMP_CWORD]}" - case "$cur" in - --*) - __gitcomp "--cached --stat --numstat --shortstat --summary +__git_diff_common_options="--stat --numstat --shortstat --summary --patch-with-stat --name-only --name-status --color --no-color --color-words --no-renames --check --full-index --binary --abbrev --diff-filter= @@ -789,8 +782,20 @@ _git_diff () --ignore-all-space --exit-code --quiet --ext-diff --no-ext-diff --no-prefix --src-prefix= --dst-prefix= - --base --ours --theirs --inter-hunk-context= + --raw +" + +_git_diff () +{ + __git_has_doubledash && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "--cached --pickaxe-all --pickaxe-regex + --base --ours --theirs + $__git_diff_common_options " return ;; @@ -976,16 +981,15 @@ _git_log () --relative-date --date= --author= --committer= --grep= --all-match - --pretty= --name-status --name-only --raw + --pretty= --not --all --left-right --cherry-pick --graph - --stat --numstat --shortstat - --decorate --diff-filter= - --color-words --walk-reflogs + --decorate + --walk-reflogs --parents --children --full-history --merge - --inter-hunk-context= + $__git_diff_common_options --pickaxe-all --pickaxe-regex " return @@ -1490,7 +1494,9 @@ _git_show () return ;; --*) - __gitcomp "--pretty=" + __gitcomp "--pretty= + $__git_diff_common_options + " return ;; esac -- cgit v1.2.3 From f13bfc1be7d25955e3ff5563fb6e35d03a408b4e Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Tue, 20 Jan 2009 00:38:16 +0100 Subject: contrib/difftool: change trap condition from SIGINT to INT git-difftool worked for me on an up-to-date Gentoo Linux at home, but didn't work on a somewhat older Ubuntu Linux 7.10 at work and failed with the following error, where 'Makefile' was locally modified: trap: 244: SIGINT: bad trap external diff died, stopping at Makefile. In 'man 1p trap' there is written: "The condition can be EXIT, 0 (equivalent to EXIT), or a signal specified using a symbolic name, without the SIG prefix, [...]" "Implementations may permit names with the SIG prefix or ignore case in signal names as an extension." So now we do it the POSIX compliant way instead of using an extension. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index f013726d0f..a2eb59b0f0 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -53,7 +53,7 @@ launch_merge_tool () { # Create and ensure that we clean up $BACKUP test -f "$MERGED" && cp -- "$MERGED" "$BACKUP" - trap sigint_handler SIGINT + trap sigint_handler INT # $LOCAL and $REMOTE are temporary files so prompt # the user with the real $MERGED name before launching $merge_tool. -- cgit v1.2.3 From bc08fc4e850794c76da367bb628e508e6d41b1eb Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Tue, 20 Jan 2009 00:41:18 +0100 Subject: contrib/difftool: remove distracting 'echo' in the SIGINT handler When interrupting git-difftool with Ctrl-C, the output of this echo command led to having the cursor at the beginning of the line below the shell prompt. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 1 - 1 file changed, 1 deletion(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index a2eb59b0f0..0c48506eeb 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -32,7 +32,6 @@ cleanup_temp_files () { # This is called when users Ctrl-C out of git-difftool-helper sigint_handler () { - echo cleanup_temp_files exit 1 } -- cgit v1.2.3 From f135e72d611ff6faf3d413a85f1620227d9f0705 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Wed, 21 Jan 2009 20:14:55 +0100 Subject: bash completion: add 'rename' subcommand to git-remote Signed-off-by: Markus Heidelberg Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a1298c4f96..703f4c2e90 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1391,7 +1391,7 @@ _git_config () _git_remote () { - local subcommands="add rm show prune update" + local subcommands="add rename rm show prune update" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" @@ -1399,7 +1399,7 @@ _git_remote () fi case "$subcommand" in - rm|show|prune) + rename|rm|show|prune) __gitcomp "$(__git_remotes)" ;; update) -- cgit v1.2.3 From 384770a5e79938b6a7633c5996597ef3211e4a7c Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Sat, 31 Jan 2009 00:19:29 +0100 Subject: contrib/difftool: add support for Kompare Signed-off-by: Markus Heidelberg Acked-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 16 ++++++++++------ contrib/difftool/git-difftool.txt | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index 0c48506eeb..10632a3917 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -1,7 +1,7 @@ #!/bin/sh # git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher. -# It supports kdiff3, tkdiff, xxdiff, meld, opendiff, emerge, ecmerge, -# vimdiff, gvimdiff, and custom user-configurable tools. +# It supports kdiff3, kompare, tkdiff, xxdiff, meld, opendiff, +# emerge, ecmerge, vimdiff, gvimdiff, and custom user-configurable tools. # This script is typically launched by using the 'git difftool' # convenience command. # @@ -73,6 +73,10 @@ launch_merge_tool () { > /dev/null 2>&1 ;; + kompare) + "$merge_tool_path" "$LOCAL" "$REMOTE" + ;; + tkdiff) "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE" ;; @@ -134,7 +138,7 @@ valid_custom_tool() { # Built-in merge tools are always valid. valid_tool() { case "$1" in - kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) + kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) ;; # happy *) if ! valid_custom_tool "$1" @@ -180,14 +184,14 @@ if test -z "$merge_tool"; then # We have a $DISPLAY so try some common UNIX merge tools if test -n "$DISPLAY"; then - merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff" + merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff" # If gnome then prefer meld if test -n "$GNOME_DESKTOP_SESSION_ID"; then merge_tool_candidates="meld $merge_tool_candidates" fi - # If KDE then prefer kdiff3 + # If KDE then prefer kdiff3 or kompare if test "$KDE_FULL_SESSION" = "true"; then - merge_tool_candidates="kdiff3 $merge_tool_candidates" + merge_tool_candidates="kdiff3 kompare $merge_tool_candidates" fi fi diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index ca3dbd2465..6e2610cda6 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -28,7 +28,8 @@ OPTIONS --tool=:: Use the merge resolution program specified by . Valid merge tools are: - kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff + kdiff3, kompare, tkdiff, meld, xxdiff, emerge, + vimdiff, gvimdiff, ecmerge, and opendiff + If a merge resolution program is not specified, 'git-difftool' will use the configuration variable `merge.tool`. If the -- cgit v1.2.3 From 99ccabaffa201e867f2073947dcccae3947ec4f1 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sat, 31 Jan 2009 12:27:56 -0800 Subject: contrib/difftool: Don't repeat merge tool candidates git difftool listed some candidates for mergetools twice, depending on the environment. This slightly changes the behavior when both KDE_FULL_SESSION and GNOME_DESKTOP_SESSION_ID are set at the same time; in such a case meld is used in favor of kdiff3 (the old code favored kdiff3 in such a case), but it should not matter in practice. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index 10632a3917..db3af6a833 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -181,31 +181,24 @@ fi # Try to guess an appropriate merge tool if no tool has been set. if test -z "$merge_tool"; then - # We have a $DISPLAY so try some common UNIX merge tools if test -n "$DISPLAY"; then - merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff" - # If gnome then prefer meld - if test -n "$GNOME_DESKTOP_SESSION_ID"; then - merge_tool_candidates="meld $merge_tool_candidates" - fi - # If KDE then prefer kdiff3 or kompare - if test "$KDE_FULL_SESSION" = "true"; then - merge_tool_candidates="kdiff3 kompare $merge_tool_candidates" + # If gnome then prefer meld, otherwise, prefer kdiff3 or kompare + if test -n "$GNOME_DESKTOP_SESSION_ID" ; then + merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff" + else + merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff" fi fi - - # $EDITOR is emacs so add emerge as a candidate if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then - merge_tool_candidates="$merge_tool_candidates emerge" + # $EDITOR is emacs so add emerge as a candidate + merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff" + elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then + # $EDITOR is vim so add vimdiff as a candidate + merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge" + else + merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" fi - - # $EDITOR is vim so add vimdiff as a candidate - if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then - merge_tool_candidates="$merge_tool_candidates vimdiff" - fi - - merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" echo "merge tool candidates: $merge_tool_candidates" # Loop over each candidate and stop when a valid merge tool is found. -- cgit v1.2.3 From 738a94a9f645a5f63fbccaab76d5e43a97e53d78 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Tue, 3 Feb 2009 10:20:54 +0100 Subject: bash: offer to show (un)staged changes Add a bit of code to __git_ps1 that lets it append '*' to the branch name if there are any unstaged changes, and '+' if there are any staged changes. Since this is a rather expensive operation and will force a lot of data into the cache whenever you first enter a repository, you have to enable it manually by setting GIT_PS1_SHOWDIRTYSTATE to a nonempty value. The configuration variable bash.showDirtyState can then be used to disable it again for some repositories. Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 81f70ec644..307bf5d4f9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -34,6 +34,12 @@ # are currently in a git repository. The %s token will be # the name of the current branch. # +# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty +# value, unstaged (*) and staged (+) changes will be shown next +# to the branch name. You can configure this per-repository +# with the bash.showDirtyState variable, which defaults to true +# once GIT_PS1_SHOWDIRTYSTATE is enabled. +# # To submit patches: # # *) Read Documentation/SubmittingPatches @@ -116,10 +122,26 @@ __git_ps1 () fi fi + local w + local i + + if test -n "$GIT_PS1_SHOWDIRTYSTATE"; then + if test "$(git config --bool bash.showDirtyState)" != "false"; then + git diff --no-ext-diff --ignore-submodules \ + --quiet --exit-code || w="*" + if git rev-parse --quiet --verify HEAD >/dev/null; then + git diff-index --cached --quiet \ + --ignore-submodules HEAD -- || i="+" + else + i="#" + fi + fi + fi + if [ -n "${1-}" ]; then - printf "$1" "${b##refs/heads/}$r" + printf "$1" "${b##refs/heads/}$w$i$r" else - printf " (%s)" "${b##refs/heads/}$r" + printf " (%s)" "${b##refs/heads/}$w$i$r" fi fi } -- cgit v1.2.3 From e1ff064e1bffedb917c491681b7bfd4d66633ef5 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Wed, 4 Feb 2009 11:04:18 +0100 Subject: contrib git-resurrect: find traces of a branch name and resurrect it Add a tool 'git-resurrect.sh ' that tries to find traces of the in the HEAD reflog and, optionally, all merge commits in the repository. It can then resurrect the branch, pointing it at the most recent of all candidate commits found. Signed-off-by: Thomas Rast Signed-off-by: Junio C Hamano --- contrib/git-resurrect.sh | 180 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100755 contrib/git-resurrect.sh (limited to 'contrib') diff --git a/contrib/git-resurrect.sh b/contrib/git-resurrect.sh new file mode 100755 index 0000000000..c364dda696 --- /dev/null +++ b/contrib/git-resurrect.sh @@ -0,0 +1,180 @@ +#!/bin/sh + +USAGE="[-a] [-r] [-m] [-t] [-n] [-b ] " +LONG_USAGE="git-resurrect attempts to find traces of a branch tip +called , and tries to resurrect it. Currently, the reflog is +searched for checkout messages, and with -r also merge messages. With +-m and -t, the history of all refs is scanned for Merge into +other/Merge into (respectively) commit subjects, which +is rather slow but allows you to resurrect other people's topic +branches." + +OPTIONS_SPEC="\ +git resurrect $USAGE +-- +b,branch= save branch as instead of +a,all same as -l -r -m -t +k,keep-going full rev-list scan (instead of first match) +l,reflog scan reflog for checkouts (enabled by default) +r,reflog-merges scan for merges recorded in reflog +m,merges scan for merges into other branches (slow) +t,merge-targets scan for merges of other branches into +n,dry-run don't recreate the branch" + +. git-sh-setup + +search_reflog () { + sed -ne 's~^\([^ ]*\) .*\tcheckout: moving from '"$1"' .*~\1~p' \ + < "$GIT_DIR"/logs/HEAD +} + +search_reflog_merges () { + git rev-parse $( + sed -ne 's~^[^ ]* \([^ ]*\) .*\tmerge '"$1"':.*~\1^2~p' \ + < "$GIT_DIR"/logs/HEAD + ) +} + +_x40="[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]" +_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" + +search_merges () { + git rev-list --all --grep="Merge branch '$1'" \ + --pretty=tformat:"%P %s" | + sed -ne "/^$_x40 \($_x40\) Merge .*/ {s//\1/p;$early_exit}" +} + +search_merge_targets () { + git rev-list --all --grep="Merge branch '[^']*' into $branch\$" \ + --pretty=tformat:"%H %s" --all | + sed -ne "/^\($_x40\) Merge .*/ {s//\1/p;$early_exit} " +} + +dry_run= +early_exit=q +scan_reflog=t +scan_reflog_merges= +scan_merges= +scan_merge_targets= +new_name= + +while test "$#" != 0; do + case "$1" in + -b|--branch) + shift + new_name="$1" + ;; + -n|--dry-run) + dry_run=t + ;; + --no-dry-run) + dry_run= + ;; + -k|--keep-going) + early_exit= + ;; + --no-keep-going) + early_exit=q + ;; + -m|--merges) + scan_merges=t + ;; + --no-merges) + scan_merges= + ;; + -l|--reflog) + scan_reflog=t + ;; + --no-reflog) + scan_reflog= + ;; + -r|--reflog_merges) + scan_reflog_merges=t + ;; + --no-reflog_merges) + scan_reflog_merges= + ;; + -t|--merge-targets) + scan_merge_targets=t + ;; + --no-merge-targets) + scan_merge_targets= + ;; + -a|--all) + scan_reflog=t + scan_reflog_merges=t + scan_merges=t + scan_merge_targets=t + ;; + --) + shift + break + ;; + *) + usage + ;; + esac + shift +done + +test "$#" = 1 || usage + +all_strategies="$scan_reflog$scan_reflog_merges$scan_merges$scan_merge_targets" +if test -z "$all_strategies"; then + die "must enable at least one of -lrmt" +fi + +branch="$1" +test -z "$new_name" && new_name="$branch" + +if test ! -z "$scan_reflog"; then + if test -r "$GIT_DIR"/logs/HEAD; then + candidates="$(search_reflog $branch)" + else + die 'reflog scanning requested, but' \ + '$GIT_DIR/logs/HEAD not readable' + fi +fi +if test ! -z "$scan_reflog_merges"; then + if test -r "$GIT_DIR"/logs/HEAD; then + candidates="$candidates $(search_reflog_merges $branch)" + else + die 'reflog scanning requested, but' \ + '$GIT_DIR/logs/HEAD not readable' + fi +fi +if test ! -z "$scan_merges"; then + candidates="$candidates $(search_merges $branch)" +fi +if test ! -z "$scan_merge_targets"; then + candidates="$candidates $(search_merge_targets $branch)" +fi + +candidates="$(git rev-parse $candidates | sort -u)" + +if test -z "$candidates"; then + hint= + test "z$all_strategies" != "ztttt" \ + && hint=" (maybe try again with -a)" + die "no candidates for $branch found$hint" +fi + +echo "** Candidates for $branch **" +for cmt in $candidates; do + git --no-pager log --pretty=tformat:"%ct:%h [%cr] %s" --abbrev-commit -1 $cmt +done \ +| sort -n | cut -d: -f2- + +newest="$(git rev-list -1 $candidates)" +if test ! -z "$dry_run"; then + printf "** Most recent: " + git --no-pager log -1 --pretty=tformat:"%h %s" $newest +elif ! git rev-parse --verify --quiet $new_name >/dev/null; then + printf "** Restoring $new_name to " + git --no-pager log -1 --pretty=tformat:"%h %s" $newest + git branch $new_name $newest +else + printf "Most recent: " + git --no-pager log -1 --pretty=tformat:"%h %s" $newest + echo "** $new_name already exists, doing nothing" +fi -- cgit v1.2.3 From c375e9d04cbcaaca7ae459437d185eda0a4472b4 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 23 Nov 2008 14:16:22 +0100 Subject: git.el: Add a checkout command. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prompts for a branch name and checks it out. Bound to C-c C-o by default. Based on a patch by Rémi Vanicat . Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 09e8bae3a4..5ce9bf19a7 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -39,10 +39,8 @@ ;; - renaming files from the status buffer ;; - creating tags ;; - fetch/pull -;; - switching branches ;; - revlist browser ;; - git-show-branch browser -;; - menus ;; (eval-when-compile (require 'cl)) @@ -397,6 +395,17 @@ the process output as a string, or nil if the git command failed." (unless newval (push "-d" args)) (apply 'git-call-process-display-error "update-ref" args))) +(defun git-for-each-ref (&rest specs) + "Return a list of refs using git-for-each-ref. +Each entry is a cons of (SHORT-NAME . FULL-NAME)." + (let (refs) + (with-temp-buffer + (apply #'git-call-process t "for-each-ref" "--format=%(refname)" specs) + (goto-char (point-min)) + (while (re-search-forward "^[^/\n]+/[^/\n]+/\\(.+\\)$" nil t) + (push (cons (match-string 1) (match-string 0)) refs))) + (nreverse refs))) + (defun git-read-tree (tree &optional index-file) "Read a tree into the index file." (let ((process-environment @@ -1356,6 +1365,24 @@ Return the list of files that haven't been handled." (push (match-string 1) files))) files)) +(defun git-read-commit-name (prompt &optional default) + "Ask for a commit name, with completion for local branch, remote branch and tag." + (completing-read prompt + (list* "HEAD" "ORIG_HEAD" "FETCH_HEAD" (mapcar #'car (git-for-each-ref))) + nil nil nil nil default)) + +(defun git-checkout (branch &optional merge) + "Checkout a branch, tag, or any commit. +Use a prefix arg if git should merge while checking out." + (interactive + (list (git-read-commit-name "Checkout: ") + current-prefix-arg)) + (unless git-status (error "Not in git-status buffer.")) + (let ((args (list branch "--"))) + (when merge (push "-m" args)) + (when (apply #'git-call-process-display-error "checkout" args) + (git-update-status-files)))) + (defun git-amend-commit () "Undo the last commit on HEAD, and set things up to commit an amended version of it." @@ -1471,6 +1498,7 @@ amended version of it." (define-key map "\M-\C-?" 'git-unmark-all) ; the commit submap (define-key commit-map "\C-a" 'git-amend-commit) + (define-key commit-map "\C-o" 'git-checkout) ; the diff submap (define-key diff-map "b" 'git-diff-file-base) (define-key diff-map "c" 'git-diff-file-combined) @@ -1491,6 +1519,7 @@ amended version of it." `("Git" ["Refresh" git-refresh-status t] ["Commit" git-commit-file t] + ["Checkout..." git-checkout t] ("Merge" ["Next Unmerged File" git-next-unmerged-file t] ["Prev Unmerged File" git-prev-unmerged-file t] -- cgit v1.2.3 From 811b10c746a63d1818d52c9ecbf247d9a3891597 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 23 Nov 2008 14:25:50 +0100 Subject: git.el: Add a command to create a new branch. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prompts for a branch name, create a new branch at HEAD and switch to it. Bound to C-c C-b by default. Based on a patch by Rémi Vanicat . Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 5ce9bf19a7..6727ff54be 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1383,6 +1383,18 @@ Use a prefix arg if git should merge while checking out." (when (apply #'git-call-process-display-error "checkout" args) (git-update-status-files)))) +(defun git-branch (branch) + "Create a branch from the current HEAD and switch to it." + (interactive (list (git-read-commit-name "Branch: "))) + (unless git-status (error "Not in git-status buffer.")) + (if (git-rev-parse (concat "refs/heads/" branch)) + (if (yes-or-no-p (format "Branch %s already exists, replace it? " branch)) + (and (git-call-process-display-error "branch" "-f" branch) + (git-call-process-display-error "checkout" branch)) + (message "Canceled.")) + (git-call-process-display-error "checkout" "-b" branch)) + (git-refresh-ewoc-hf git-status)) + (defun git-amend-commit () "Undo the last commit on HEAD, and set things up to commit an amended version of it." @@ -1498,6 +1510,7 @@ amended version of it." (define-key map "\M-\C-?" 'git-unmark-all) ; the commit submap (define-key commit-map "\C-a" 'git-amend-commit) + (define-key commit-map "\C-b" 'git-branch) (define-key commit-map "\C-o" 'git-checkout) ; the diff submap (define-key diff-map "b" 'git-diff-file-base) @@ -1520,6 +1533,7 @@ amended version of it." ["Refresh" git-refresh-status t] ["Commit" git-commit-file t] ["Checkout..." git-checkout t] + ["New Branch..." git-branch t] ("Merge" ["Next Unmerged File" git-next-unmerged-file t] ["Prev Unmerged File" git-prev-unmerged-file t] -- cgit v1.2.3 From ab69e3e43a12cf02505f3e9e561c49c1fe8a81a6 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 23 Nov 2008 14:34:48 +0100 Subject: git.el: Add commands for cherry-pick and revert. Support for cherry-picking and reverting commits, with automatic formatting of the commit log message. Bound to C-c C-p and C-c C-v respectively. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 6727ff54be..b7ea636534 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1411,6 +1411,44 @@ amended version of it." (git-setup-commit-buffer commit) (git-commit-file)))) +(defun git-cherry-pick-commit (arg) + "Cherry-pick a commit." + (interactive (list (git-read-commit-name "Cherry-pick commit: "))) + (unless git-status (error "Not in git-status buffer.")) + (let ((commit (git-rev-parse (concat arg "^0")))) + (unless commit (error "Not a valid commit '%s'." arg)) + (when (git-rev-parse (concat commit "^2")) + (error "Cannot cherry-pick a merge commit.")) + (let ((files (git-get-commit-files commit)) + (ok (git-call-process-display-error "cherry-pick" "-n" commit))) + (git-update-status-files files ok) + (with-current-buffer (git-setup-commit-buffer commit) + (goto-char (point-min)) + (if (re-search-forward "^\n*Signed-off-by:" nil t 1) + (goto-char (match-beginning 0)) + (goto-char (point-max))) + (insert "(cherry picked from commit " commit ")\n")) + (when ok (git-commit-file))))) + +(defun git-revert-commit (arg) + "Revert a commit." + (interactive (list (git-read-commit-name "Revert commit: "))) + (unless git-status (error "Not in git-status buffer.")) + (let ((commit (git-rev-parse (concat arg "^0")))) + (unless commit (error "Not a valid commit '%s'." arg)) + (when (git-rev-parse (concat commit "^2")) + (error "Cannot revert a merge commit.")) + (let ((files (git-get-commit-files commit)) + (subject (git-get-commit-description commit)) + (ok (git-call-process-display-error "revert" "-n" commit))) + (git-update-status-files files ok) + (when (string-match "^[0-9a-f]+ - \\(.*\\)$" subject) + (setq subject (match-string 1 subject))) + (git-setup-log-buffer (get-buffer-create "*git-commit*") + (git-get-merge-heads) nil nil (format "Revert \"%s\"" subject) nil + (format "This reverts commit %s.\n" commit)) + (when ok (git-commit-file))))) + (defun git-find-file () "Visit the current file in its own buffer." (interactive) @@ -1512,6 +1550,8 @@ amended version of it." (define-key commit-map "\C-a" 'git-amend-commit) (define-key commit-map "\C-b" 'git-branch) (define-key commit-map "\C-o" 'git-checkout) + (define-key commit-map "\C-p" 'git-cherry-pick-commit) + (define-key commit-map "\C-v" 'git-revert-commit) ; the diff submap (define-key diff-map "b" 'git-diff-file-base) (define-key diff-map "c" 'git-diff-file-combined) @@ -1534,6 +1574,8 @@ amended version of it." ["Commit" git-commit-file t] ["Checkout..." git-checkout t] ["New Branch..." git-branch t] + ["Cherry-pick Commit..." git-cherry-pick-commit t] + ["Revert Commit..." git-revert-commit t] ("Merge" ["Next Unmerged File" git-next-unmerged-file t] ["Prev Unmerged File" git-prev-unmerged-file t] -- cgit v1.2.3 From a7da5c425970372f75d7cc2c194d5646554f8a32 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sun, 23 Nov 2008 16:12:45 +0100 Subject: git.el: Make git-run-command-region display the error if any. This makes it easier to figure out why a commit has failed. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index b7ea636534..415765ec51 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -220,7 +220,7 @@ the process output as a string, or nil if the git command failed." (with-current-buffer buffer (cd dir) (apply #'call-process-region start end program - nil (list output-buffer nil) nil args)))) + nil (list output-buffer t) nil args)))) (defun git-run-command-buffer (buffer-name &rest args) "Run a git command, sending the output to a buffer named BUFFER-NAME." @@ -237,13 +237,15 @@ the process output as a string, or nil if the git command failed." (defun git-run-command-region (buffer start end env &rest args) "Run a git command with specified buffer region as input." - (unless (eq 0 (if env - (git-run-process-region - buffer start end "env" - (append (git-get-env-strings env) (list "git") args)) + (with-temp-buffer + (if (eq 0 (if env (git-run-process-region - buffer start end "git" args))) - (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))) + buffer start end "env" + (append (git-get-env-strings env) (list "git") args)) + (git-run-process-region buffer start end "git" args))) + (buffer-string) + (display-message-or-buffer (current-buffer)) + nil))) (defun git-run-hook (hook env &rest args) "Run a git hook and display its output if any." @@ -456,18 +458,16 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (setq coding-system-for-write buffer-file-coding-system)) (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)))) + (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)))))) + (when commit (git-update-ref "HEAD" commit head subject)) + commit))) (defun git-empty-db-p () "Check if the git db is empty (no commit done yet)." -- cgit v1.2.3 From efd49f50fc087df2ad46f194ca848c5335f4cca9 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Tue, 27 Jan 2009 11:59:54 +0100 Subject: git.el: Set a regexp for paragraph-separate in log-edit mode. This allows using fill-paragraph on the log message without interference from the various header fields. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 415765ec51..f86c437518 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1329,6 +1329,7 @@ Return the list of files that haven't been handled." (log-edit-diff-function . git-log-edit-diff)) buffer) (log-edit 'git-do-commit nil 'git-log-edit-files buffer)) (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords)) + (setq paragraph-separate (concat (regexp-quote git-log-msg-separator) "$\\|Author: \\|Date: \\|Merge: \\|Signed-off-by: \\|\f\\|[ ]*$")) (setq buffer-file-coding-system coding-system) (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))) -- cgit v1.2.3 From 6c4f70d5b2fb8f9275ca85e0927f00b8bc892819 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 7 Feb 2009 14:01:26 +0100 Subject: git.el: Use integer instead of character constants in case statement. This is for compatibility with XEmacs. Reported by Vassili Karpov. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index f86c437518..7651a0a8e1 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -571,29 +571,29 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (let* ((old-type (lsh (or old-perm 0) -9)) (new-type (lsh (or new-perm 0) -9)) (str (case new-type - (?\100 ;; file + (64 ;; file (case old-type - (?\100 nil) - (?\120 " (type change symlink -> file)") - (?\160 " (type change subproject -> file)"))) - (?\120 ;; symlink + (64 nil) + (80 " (type change symlink -> file)") + (112 " (type change subproject -> file)"))) + (80 ;; symlink (case old-type - (?\100 " (type change file -> symlink)") - (?\160 " (type change subproject -> symlink)") + (64 " (type change file -> symlink)") + (112 " (type change subproject -> symlink)") (t " (symlink)"))) - (?\160 ;; subproject + (112 ;; subproject (case old-type - (?\100 " (type change file -> subproject)") - (?\120 " (type change symlink -> subproject)") + (64 " (type change file -> subproject)") + (80 " (type change symlink -> subproject)") (t " (subproject)"))) - (?\110 nil) ;; directory (internal, not a real git state) - (?\000 ;; deleted or unknown + (72 nil) ;; directory (internal, not a real git state) + (0 ;; deleted or unknown (case old-type - (?\120 " (symlink)") - (?\160 " (subproject)"))) + (80 " (symlink)") + (112 " (subproject)"))) (t (format " (unknown type %o)" new-type))))) (cond (str (propertize str 'face 'git-status-face)) - ((eq new-type ?\110) "/") + ((eq new-type 72) "/") (t "")))) (defun git-rename-as-string (info) -- cgit v1.2.3 From 5a7b3bf5275adf86fdd23f8824562b88c8a20e33 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 7 Feb 2009 14:21:58 +0100 Subject: git.el: Add some notes about Emacs versions compatibility. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 7651a0a8e1..fcbe2d9cf5 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1,6 +1,6 @@ ;;; git.el --- A user interface for git -;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard +;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Alexandre Julliard ;; Version: 1.0 @@ -34,7 +34,6 @@ ;; To start: `M-x git-status' ;; ;; TODO -;; - portability to XEmacs ;; - diff against other branch ;; - renaming files from the status buffer ;; - creating tags @@ -43,6 +42,15 @@ ;; - git-show-branch browser ;; +;;; Compatibility: +;; +;; This file works on GNU Emacs 21 or later. It may work on older +;; versions but this is not guaranteed. +;; +;; It may work on XEmacs 21, provided that you first install the ewoc +;; and log-edit packages. +;; + (eval-when-compile (require 'cl)) (require 'ewoc) (require 'log-edit) -- cgit v1.2.3 From 7851386948dce72c739bcdfe08f069afe4f5ea45 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Sat, 7 Feb 2009 14:24:54 +0100 Subject: emacs: Remove the no longer maintained vc-git package. vc-git is distributed with Emacs since version 22.2, and is maintained in the Emacs CVS tree. This file is obsolete and causes trouble for people who want to add contrib/emacs to their load-path. Signed-off-by: Alexandre Julliard --- contrib/emacs/Makefile | 2 +- contrib/emacs/vc-git.el | 216 ------------------------------------------------ 2 files changed, 1 insertion(+), 217 deletions(-) delete mode 100644 contrib/emacs/vc-git.el (limited to 'contrib') diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile index a48540a92b..24d9312941 100644 --- a/contrib/emacs/Makefile +++ b/contrib/emacs/Makefile @@ -2,7 +2,7 @@ EMACS = emacs -ELC = git.elc vc-git.elc git-blame.elc +ELC = git.elc git-blame.elc INSTALL ?= install INSTALL_ELC = $(INSTALL) -m 644 prefix ?= $(HOME) diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el deleted file mode 100644 index b8f6be5c0a..0000000000 --- a/contrib/emacs/vc-git.el +++ /dev/null @@ -1,216 +0,0 @@ -;;; vc-git.el --- VC backend for the git version control system - -;; Copyright (C) 2006 Alexandre Julliard - -;; This program is free software; you can redistribute it and/or -;; modify it under the terms of the GNU General Public License as -;; published by the Free Software Foundation; either version 2 of -;; the License, or (at your option) any later version. -;; -;; This program is distributed in the hope that it will be -;; useful, but WITHOUT ANY WARRANTY; without even the implied -;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -;; PURPOSE. See the GNU General Public License for more details. -;; -;; You should have received a copy of the GNU General Public -;; License along with this program; if not, write to the Free -;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, -;; MA 02111-1307 USA - -;;; Commentary: - -;; This file contains a VC backend for the git version control -;; system. -;; -;; To install: put this file on the load-path and add GIT to the list -;; of supported backends in `vc-handled-backends'; the following line, -;; placed in your ~/.emacs, will accomplish this: -;; -;; (add-to-list 'vc-handled-backends 'GIT) -;; -;; TODO -;; - changelog generation -;; - working with revisions other than HEAD -;; - -(eval-when-compile (require 'cl)) - -(defvar git-commits-coding-system 'utf-8 - "Default coding system for git commits.") - -(defun vc-git--run-command-string (file &rest args) - "Run a git command on FILE and return its output as string." - (let* ((ok t) - (str (with-output-to-string - (with-current-buffer standard-output - (unless (eq 0 (apply #'call-process "git" nil '(t nil) nil - (append args (list (file-relative-name file))))) - (setq ok nil)))))) - (and ok str))) - -(defun vc-git--run-command (file &rest args) - "Run a git command on FILE, discarding any output." - (let ((name (file-relative-name file))) - (eq 0 (apply #'call-process "git" nil (get-buffer "*Messages") nil (append args (list name)))))) - -(defun vc-git-registered (file) - "Check whether FILE is registered with git." - (with-temp-buffer - (let* ((dir (file-name-directory file)) - (name (file-relative-name file dir))) - (and (ignore-errors - (when dir (cd dir)) - (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name))) - (let ((str (buffer-string))) - (and (> (length str) (length name)) - (string= (substring str 0 (1+ (length name))) (concat name "\0")))))))) - -(defun vc-git-state (file) - "git-specific version of `vc-state'." - (let ((diff (vc-git--run-command-string file "diff-index" "-z" "HEAD" "--"))) - (if (and diff (string-match ":[0-7]\\{6\\} [0-7]\\{6\\} [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} [ADMU]\0[^\0]+\0" diff)) - 'edited - 'up-to-date))) - -(defun vc-git-workfile-version (file) - "git-specific version of `vc-workfile-version'." - (let ((str (with-output-to-string - (with-current-buffer standard-output - (call-process "git" nil '(t nil) nil "symbolic-ref" "HEAD"))))) - (if (string-match "^\\(refs/heads/\\)?\\(.+\\)$" str) - (match-string 2 str) - str))) - -(defun vc-git-symbolic-commit (commit) - "Translate COMMIT string into symbolic form. -Returns nil if not possible." - (and commit - (with-temp-buffer - (and - (zerop - (call-process "git" nil '(t nil) nil "name-rev" - "--name-only" "--tags" - commit)) - (goto-char (point-min)) - (= (forward-line 2) 1) - (bolp) - (buffer-substring-no-properties (point-min) (1- (point-max))))))) - -(defun vc-git-previous-version (file rev) - "git-specific version of `vc-previous-version'." - (let ((default-directory (file-name-directory (expand-file-name file))) - (file (file-name-nondirectory file))) - (vc-git-symbolic-commit - (with-temp-buffer - (and - (zerop - (call-process "git" nil '(t nil) nil "rev-list" - "-2" rev "--" file)) - (goto-char (point-max)) - (bolp) - (zerop (forward-line -1)) - (not (bobp)) - (buffer-substring-no-properties - (point) - (1- (point-max)))))))) - -(defun vc-git-next-version (file rev) - "git-specific version of `vc-next-version'." - (let* ((default-directory (file-name-directory - (expand-file-name file))) - (file (file-name-nondirectory file)) - (current-rev - (with-temp-buffer - (and - (zerop - (call-process "git" nil '(t nil) nil "rev-list" - "-1" rev "--" file)) - (goto-char (point-max)) - (bolp) - (zerop (forward-line -1)) - (bobp) - (buffer-substring-no-properties - (point) - (1- (point-max))))))) - (and current-rev - (vc-git-symbolic-commit - (with-temp-buffer - (and - (zerop - (call-process "git" nil '(t nil) nil "rev-list" - "HEAD" "--" file)) - (goto-char (point-min)) - (search-forward current-rev nil t) - (zerop (forward-line -1)) - (buffer-substring-no-properties - (point) - (progn (forward-line 1) (1- (point)))))))))) - -(defun vc-git-revert (file &optional contents-done) - "Revert FILE to the version stored in the git repository." - (if contents-done - (vc-git--run-command file "update-index" "--") - (vc-git--run-command file "checkout" "HEAD"))) - -(defun vc-git-checkout-model (file) - 'implicit) - -(defun vc-git-workfile-unchanged-p (file) - (let ((sha1 (vc-git--run-command-string file "hash-object" "--")) - (head (vc-git--run-command-string file "ls-tree" "-z" "HEAD" "--"))) - (and head - (string-match "[0-7]\\{6\\} blob \\([0-9a-f]\\{40\\}\\)\t[^\0]+\0" head) - (string= (car (split-string sha1 "\n")) (match-string 1 head))))) - -(defun vc-git-register (file &optional rev comment) - "Register FILE into the git version-control system." - (vc-git--run-command file "update-index" "--add" "--")) - -(defun vc-git-print-log (file &optional buffer) - (let ((name (file-relative-name file)) - (coding-system-for-read git-commits-coding-system)) - (vc-do-command buffer 'async "git" name "rev-list" "--pretty" "HEAD" "--"))) - -(defun vc-git-diff (file &optional rev1 rev2 buffer) - (let ((name (file-relative-name file)) - (buf (or buffer "*vc-diff*"))) - (if (and rev1 rev2) - (vc-do-command buf 0 "git" name "diff-tree" "-p" rev1 rev2 "--") - (vc-do-command buf 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--")) - ; git-diff-index doesn't set exit status like diff does - (if (vc-git-workfile-unchanged-p file) 0 1))) - -(defun vc-git-checkin (file rev comment) - (let ((coding-system-for-write git-commits-coding-system)) - (vc-git--run-command file "commit" "-m" comment "--only" "--"))) - -(defun vc-git-checkout (file &optional editable rev destfile) - (if destfile - (let ((fullname (substring - (vc-git--run-command-string file "ls-files" "-z" "--full-name" "--") - 0 -1)) - (coding-system-for-read 'no-conversion) - (coding-system-for-write 'no-conversion)) - (with-temp-file destfile - (eq 0 (call-process "git" nil t nil "cat-file" "blob" - (concat (or rev "HEAD") ":" fullname))))) - (vc-git--run-command file "checkout" (or rev "HEAD")))) - -(defun vc-git-annotate-command (file buf &optional rev) - ; FIXME: rev is ignored - (let ((name (file-relative-name file))) - (call-process "git" nil buf nil "blame" name))) - -(defun vc-git-annotate-time () - (and (re-search-forward "[0-9a-f]+ (.* \\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\) +[0-9]+)" nil t) - (vc-annotate-convert-time - (apply #'encode-time (mapcar (lambda (match) (string-to-number (match-string match))) '(6 5 4 3 2 1 7)))))) - -;; Not really useful since we can't do anything with the revision yet -;;(defun vc-annotate-extract-revision-at-line () -;; (save-excursion -;; (move-beginning-of-line 1) -;; (and (looking-at "[0-9a-f]+") -;; (buffer-substring (match-beginning 0) (match-end 0))))) - -(provide 'vc-git) -- cgit v1.2.3 From cf9957875c3a27b6ae4593e1fa9d4dabbde68433 Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Fri, 6 Feb 2009 11:05:37 -0500 Subject: completion: Fix GIT_PS1_SHOWDIRTYSTATE to prevent unbound variable errors. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 307bf5d4f9..6e04985079 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -125,7 +125,7 @@ __git_ps1 () local w local i - if test -n "$GIT_PS1_SHOWDIRTYSTATE"; then + if test -n "${GIT_PS1_SHOWDIRTYSTATE-}"; then if test "$(git config --bool bash.showDirtyState)" != "false"; then git diff --no-ext-diff --ignore-submodules \ --quiet --exit-code || w="*" -- cgit v1.2.3 From a9ee90d7ff9f3854b3096b4abbdc2013708704f5 Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Fri, 6 Feb 2009 11:05:38 -0500 Subject: completion: Get rid of tabbed indentation in comments. Replace with spaces. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6e04985079..f44f63cfeb 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -34,11 +34,11 @@ # are currently in a git repository. The %s token will be # the name of the current branch. # -# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty -# value, unstaged (*) and staged (+) changes will be shown next -# to the branch name. You can configure this per-repository -# with the bash.showDirtyState variable, which defaults to true -# once GIT_PS1_SHOWDIRTYSTATE is enabled. +# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty +# value, unstaged (*) and staged (+) changes will be shown next +# to the branch name. You can configure this per-repository +# with the bash.showDirtyState variable, which defaults to true +# once GIT_PS1_SHOWDIRTYSTATE is enabled. # # To submit patches: # -- cgit v1.2.3 From e5f5050ed1481c3bc27658f625a87155aed0984f Mon Sep 17 00:00:00 2001 From: Pat Notz Date: Tue, 10 Feb 2009 09:43:30 -0700 Subject: Fix contrib/hooks/post-receive-email for new duplicate branch In the show_new_revisions function, the original code: git rev-parse --not --branches | grep -v $(git rev-parse $refname) | isn't quite right since one can create a new branch and push it without any new commits. In that case, two refs will have the same sha1 but both would get filtered by the 'grep'. In the end, we'll show ALL the history which is not what we want. Instead, we should list the branches by name and remove the branch being updated and THEN pass that list through rev-parse. Revised as suggested by Jakub Narebski and Junio C Hamano to use git-for-each-ref instead of git-branch. (Thanks!) Signed-off-by: Pat Notz Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email index 28a3c0e46e..60cbab65d3 100644 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -615,7 +615,9 @@ show_new_revisions() revspec=$oldrev..$newrev fi - git rev-parse --not --branches | grep -v $(git rev-parse $refname) | + other_branches=$(git for-each-ref --format='%(refname)' refs/heads/ | + grep -F -v $refname) + git rev-parse --not $other_branches | if [ -z "$custom_showrev" ] then git rev-list --pretty --stdin $revspec -- cgit v1.2.3 From fa26a401bed5967d6118ac430c5c5f4707c54386 Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Wed, 11 Feb 2009 13:03:23 -0500 Subject: completion: For consistency, change "git rev-parse" to __gitdir calls Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f44f63cfeb..6bbe09ab9a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -80,7 +80,7 @@ __gitdir () # returns text to add to bash PS1 prompt (includes branch name) __git_ps1 () { - local g="$(git rev-parse --git-dir 2>/dev/null)" + local g="$(__gitdir)" if [ -n "$g" ]; then local r local b @@ -1797,7 +1797,7 @@ _gitk () __git_has_doubledash && return local cur="${COMP_WORDS[COMP_CWORD]}" - local g="$(git rev-parse --git-dir 2>/dev/null)" + local g="$(__gitdir)" local merge="" if [ -f $g/MERGE_HEAD ]; then merge="--merge" -- cgit v1.2.3 From ad244d256865c06804afffef32b753239a06119e Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Wed, 11 Feb 2009 13:03:24 -0500 Subject: completion: Use consistent if [...] convention, not "test" The local coding convention in bash completion is to use [...] rather than test. Additionally, if [...]; then is preferred over if [...] then and so matching "if [...]\nthen" were changed accordingly. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6bbe09ab9a..c61576fcaf 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -84,39 +84,30 @@ __git_ps1 () if [ -n "$g" ]; then local r local b - if [ -d "$g/rebase-apply" ] - then - if test -f "$g/rebase-apply/rebasing" - then + if [ -d "$g/rebase-apply" ]; then + if [ -f "$g/rebase-apply/rebasing" ]; then r="|REBASE" - elif test -f "$g/rebase-apply/applying" - then + elif [ -f "$g/rebase-apply/applying" ]; then r="|AM" else r="|AM/REBASE" fi b="$(git symbolic-ref HEAD 2>/dev/null)" - elif [ -f "$g/rebase-merge/interactive" ] - then + elif [ -f "$g/rebase-merge/interactive" ]; then r="|REBASE-i" b="$(cat "$g/rebase-merge/head-name")" - elif [ -d "$g/rebase-merge" ] - then + elif [ -d "$g/rebase-merge" ]; then r="|REBASE-m" b="$(cat "$g/rebase-merge/head-name")" - elif [ -f "$g/MERGE_HEAD" ] - then + elif [ -f "$g/MERGE_HEAD" ]; then r="|MERGING" b="$(git symbolic-ref HEAD 2>/dev/null)" else - if [ -f "$g/BISECT_LOG" ] - then + if [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi - if ! b="$(git symbolic-ref HEAD 2>/dev/null)" - then - if ! b="$(git describe --exact-match HEAD 2>/dev/null)" - then + if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then + if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then b="$(cut -c1-7 "$g/HEAD")..." fi fi @@ -125,8 +116,8 @@ __git_ps1 () local w local i - if test -n "${GIT_PS1_SHOWDIRTYSTATE-}"; then - if test "$(git config --bool bash.showDirtyState)" != "false"; then + if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then + if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then git diff --no-ext-diff --ignore-submodules \ --quiet --exit-code || w="*" if git rev-parse --quiet --verify HEAD >/dev/null; then -- cgit v1.2.3 From e5dd864adfeb8b0176b31a132e972d7f7beff32a Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Wed, 11 Feb 2009 13:03:25 -0500 Subject: completion: Better __git_ps1 support when not in working directory If .git/HEAD is not readable, __git_ps1 does nothing. If --is-in-git-dir, __git_ps1 returns " (GIT_DIR!)" as a cautionary note. The previous behavior would show the branch name (and would optionally attempt to determine the dirtyState of the directory, which was impossible because a "git diff" was used). If --is-in-work-tree, __git_ps1 returns the branch name. Additionally, if showDirtyState is on, the dirty state is displayed. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 36 +++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c61576fcaf..aa8eec24d9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -108,7 +108,9 @@ __git_ps1 () fi if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then - b="$(cut -c1-7 "$g/HEAD")..." + if [ -r "$g/HEAD" ]; then + b="$(cut -c1-7 "$g/HEAD")..." + fi fi fi fi @@ -116,23 +118,29 @@ __git_ps1 () local w local i - if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then - if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then - git diff --no-ext-diff --ignore-submodules \ - --quiet --exit-code || w="*" - if git rev-parse --quiet --verify HEAD >/dev/null; then - git diff-index --cached --quiet \ - --ignore-submodules HEAD -- || i="+" - else - i="#" + if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then + b="GIT_DIR!" + elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then + if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then + if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then + git diff --no-ext-diff --ignore-submodules \ + --quiet --exit-code || w="*" + if git rev-parse --quiet --verify HEAD >/dev/null; then + git diff-index --cached --quiet \ + --ignore-submodules HEAD -- || i="+" + else + i="#" + fi fi fi fi - if [ -n "${1-}" ]; then - printf "$1" "${b##refs/heads/}$w$i$r" - else - printf " (%s)" "${b##refs/heads/}$w$i$r" + if [ -n "$b" ]; then + if [ -n "${1-}" ]; then + printf "$1" "${b##refs/heads/}$w$i$r" + else + printf " (%s)" "${b##refs/heads/}$w$i$r" + fi fi fi } -- cgit v1.2.3 From 5c9cc64a4a608ab0bbd5eb5c8e405bfe050be309 Mon Sep 17 00:00:00 2001 From: Ted Pavlic Date: Wed, 11 Feb 2009 13:03:26 -0500 Subject: completion: More fixes to prevent unbound variable errors Several functions make use of "[-n ...]" and "[-z ...]". In many cases, the variables being tested were declared with "local." However, several __variables are not, and so they must be replaced with their ${__-} equivalents. Signed-off-by: Ted Pavlic Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index aa8eec24d9..6e8c5b91ac 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -62,7 +62,7 @@ esac __gitdir () { if [ -z "${1-}" ]; then - if [ -n "$__git_dir" ]; then + if [ -n "${__git_dir-}" ]; then echo "$__git_dir" elif [ -d .git ]; then echo .git @@ -298,7 +298,7 @@ __git_remotes () __git_merge_strategies () { - if [ -n "$__git_merge_strategylist" ]; then + if [ -n "${__git_merge_strategylist-}" ]; then echo "$__git_merge_strategylist" return fi @@ -384,7 +384,7 @@ __git_complete_revlist () __git_all_commands () { - if [ -n "$__git_all_commandlist" ]; then + if [ -n "${__git_all_commandlist-}" ]; then echo "$__git_all_commandlist" return fi @@ -402,7 +402,7 @@ __git_all_commandlist="$(__git_all_commands 2>/dev/null)" __git_porcelain_commands () { - if [ -n "$__git_porcelain_commandlist" ]; then + if [ -n "${__git_porcelain_commandlist-}" ]; then echo "$__git_porcelain_commandlist" return fi -- cgit v1.2.3 From 901d615c5d74bea20e0c8d7fcdf7585616306b79 Mon Sep 17 00:00:00 2001 From: Matt Kraai Date: Thu, 12 Feb 2009 07:55:54 -0800 Subject: bash-completion: Complete the values of color.interactive, color.ui, color.pager Signed-off-by: Matt Kraai Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f44f63cfeb..a7a10c0d79 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1196,10 +1196,14 @@ _git_config () __gitcomp "$(__git_merge_strategies)" return ;; - color.branch|color.diff|color.status) + color.branch|color.diff|color.interactive|color.status|color.ui) __gitcomp "always never auto" return ;; + color.pager) + __gitcomp "false true" + return + ;; color.*.*) __gitcomp " normal black red green yellow blue magenta cyan white -- cgit v1.2.3 From 48c9ab78f3c7a0cc51e8d17bb7e37a075996c223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sat, 14 Feb 2009 17:21:52 +0100 Subject: bash: fix misspelled 'git svn' option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit '--user-log-author' -> '--use-log-author' Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a7a10c0d79..412d2c0dab 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1610,7 +1610,7 @@ _git_svn () --follow-parent --authors-file= --repack= --no-metadata --use-svm-props --use-svnsync-props --log-window-size= --no-checkout --quiet - --repack-flags --user-log-author --localtime $remote_opts + --repack-flags --use-log-author --localtime $remote_opts " local init_opts=" --template= --shared= --trunk= --tags= -- cgit v1.2.3 From d532ebd5a799fe2c991a96004bf739434e6ecaf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sun, 15 Feb 2009 14:25:11 +0100 Subject: bash: add missing 'git merge' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Namely: '--commit', '--stat', '--no-squash', '--ff', '--no-ff'. One might wonder why add options that specify the default behaviour anyway (e.g. '--commit', '--no-squash', etc.). Users can override the default with config options (e.g. 'branch..mergeoptions', 'merge.log'), but sometimes might still need the default behaviour. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 412d2c0dab..0bb768f1c1 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1037,6 +1037,7 @@ _git_merge () --*) __gitcomp " --no-commit --no-stat --log --no-log --squash --strategy + --commit --stat --no-squash --ff --no-ff " return esac -- cgit v1.2.3 From 4a5856cb249579845e24713225bc1749a9b20482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Sat, 14 Feb 2009 17:21:53 +0100 Subject: bash: update 'git svn' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'git svn' got some new subcommands and otions in the last couple of months. This patch adds completion support for them. In particular: * 'fetch', 'clone', etc.: '--ignore-paths=' * 'init' and 'clone': '--prefix=', '--use-log-author', '--add-author-from' * 'dcommit': '--commit-url', '--revision' * 'log': '--color' * 'rebase': '--dry-run' * 'branch', 'tag', 'blame', 'migrate' subcommands and their options Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0bb768f1c1..003017ac1b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1600,7 +1600,8 @@ _git_svn () local subcommands=" init fetch clone rebase dcommit log find-rev set-tree commit-diff info create-ignore propget - proplist show-ignore show-externals + proplist show-ignore show-externals branch tag blame + migrate " local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then @@ -1611,13 +1612,15 @@ _git_svn () --follow-parent --authors-file= --repack= --no-metadata --use-svm-props --use-svnsync-props --log-window-size= --no-checkout --quiet - --repack-flags --use-log-author --localtime $remote_opts + --repack-flags --use-log-author --localtime + --ignore-paths= $remote_opts " local init_opts=" --template= --shared= --trunk= --tags= --branches= --stdlayout --minimize-url --no-metadata --use-svm-props --use-svnsync-props - --rewrite-root= $remote_opts + --rewrite-root= --prefix= --use-log-author + --add-author-from $remote_opts " local cmt_opts=" --edit --rmdir --find-copies-harder --copy-similarity= @@ -1637,7 +1640,8 @@ _git_svn () dcommit,--*) __gitcomp " --merge --strategy= --verbose --dry-run - --fetch-all --no-rebase $cmt_opts $fc_opts + --fetch-all --no-rebase --commit-url + --revision $cmt_opts $fc_opts " ;; set-tree,--*) @@ -1651,13 +1655,13 @@ _git_svn () __gitcomp " --limit= --revision= --verbose --incremental --oneline --show-commit --non-recursive - --authors-file= + --authors-file= --color " ;; rebase,--*) __gitcomp " --merge --verbose --strategy= --local - --fetch-all $fc_opts + --fetch-all --dry-run $fc_opts " ;; commit-diff,--*) @@ -1666,6 +1670,21 @@ _git_svn () info,--*) __gitcomp "--url" ;; + branch,--*) + __gitcomp "--dry-run --message --tag" + ;; + tag,--*) + __gitcomp "--dry-run --message" + ;; + blame,--*) + __gitcomp "--git-format" + ;; + migrate,--*) + __gitcomp " + --config-dir= --ignore-paths= --minimize + --no-auth-cache --username= + " + ;; *) COMPREPLY=() ;; -- cgit v1.2.3 From a393777ec9ca7c33cb76efa225ddacc53784c0dd Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Mon, 16 Feb 2009 17:34:56 +0100 Subject: bash completion: refactor common log, shortlog and gitk options Refactor options that are useful for more than one of them into a variable used by the relevant completions. This has the effect of adding the following options to git-log: --branches --tags --remotes --first-parent --dense --sparse --simplify-merges --simplify-by-decoration --first-parent --no-merges The following to git-shortlog: --branches --tags --remotes --first-parent And the following to gitk: --branches --tags --remotes --first-parent --no-merges --max-count= --max-age= --since= --after= --min-age= --until= --before= --dense --sparse --full-history --simplify-merges --simplify-by-decoration --left-right Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 49 +++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 15 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 003017ac1b..6e5260ee75 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -975,6 +975,27 @@ _git_ls_tree () __git_complete_file } +# Options that go well for log, shortlog and gitk +__git_log_common_options=" + --not --all + --branches --tags --remotes + --first-parent --no-merges + --max-count= + --max-age= --since= --after= + --min-age= --until= --before= +" +# Options that go well for log and gitk (not shortlog) +__git_log_gitk_options=" + --dense --sparse --full-history + --simplify-merges --simplify-by-decoration + --left-right +" +# Options that go well for log and shortlog (not gitk) +__git_log_shortlog_options=" + --author= --committer= --grep= + --all-match +" + __git_log_pretty_formats="oneline short medium full fuller email raw format:" _git_log () @@ -996,21 +1017,19 @@ _git_log () ;; --*) __gitcomp " - --max-count= --max-age= --since= --after= - --min-age= --before= --until= + $__git_log_common_options + $__git_log_shortlog_options + $__git_log_gitk_options --root --topo-order --date-order --reverse - --no-merges --follow + --follow --abbrev-commit --abbrev= --relative-date --date= - --author= --committer= --grep= - --all-match --pretty= - --not --all - --left-right --cherry-pick + --cherry-pick --graph --decorate --walk-reflogs - --parents --children --full-history + --parents --children --merge $__git_diff_common_options --pickaxe-all --pickaxe-regex @@ -1496,12 +1515,8 @@ _git_shortlog () case "$cur" in --*) __gitcomp " - --max-count= --max-age= --since= --after= - --min-age= --before= --until= - --no-merges - --author= --committer= --grep= - --all-match - --not --all + $__git_log_common_options + $__git_log_shortlog_options --numbered --summary " return @@ -1828,7 +1843,11 @@ _gitk () fi case "$cur" in --*) - __gitcomp "--not --all $merge" + __gitcomp " + $__git_log_common_options + $__git_log_gitk_options + $merge + " return ;; esac -- cgit v1.2.3 From bf3c20f6e855521fb92f455a9e70fbe8f107c53d Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Mon, 16 Feb 2009 17:34:57 +0100 Subject: bash completion: only show 'log --merge' if merging The gitk completion only shows --merge if MERGE_HEAD is present. Do it the same way for git-log completion. Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6e5260ee75..0a3092f646 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1003,6 +1003,11 @@ _git_log () __git_has_doubledash && return local cur="${COMP_WORDS[COMP_CWORD]}" + local g="$(git rev-parse --git-dir 2>/dev/null)" + local merge="" + if [ -f $g/MERGE_HEAD ]; then + merge="--merge" + fi case "$cur" in --pretty=*) __gitcomp "$__git_log_pretty_formats @@ -1030,7 +1035,7 @@ _git_log () --decorate --walk-reflogs --parents --children - --merge + $merge $__git_diff_common_options --pickaxe-all --pickaxe-regex " -- cgit v1.2.3 From 21ba0e84356cb73faecc4c5bf30df7b2222961d2 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 16 Feb 2009 11:39:11 +0100 Subject: git.el: Make sure that file lists are sorted as they are created. This avoids a possibly redundant sort in git-update-status-files and git-status-filenames-map, and allows callers to continue using the list without having to copy it. It also fixes the confusing success messages reported by Brent Goodrick. Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index fcbe2d9cf5..c7d15eb4dc 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -530,9 +530,9 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (git-fileinfo->needs-refresh info) t))) (defun git-status-filenames-map (status func files &rest args) - "Apply FUNC to the status files names in the FILES list." + "Apply FUNC to the status files names in the FILES list. +The list must be sorted." (when files - (setq files (sort files #'string-lessp)) (let ((file (pop files)) (node (ewoc-nth status 0))) (while (and file node) @@ -545,7 +545,7 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)." (setq file (pop files)))))))) (defun git-set-filenames-state (status files state) - "Set the state of a list of named files." + "Set the state of a list of named files. The list must be sorted" (when files (git-status-filenames-map status #'git-set-fileinfo-state files state) (unless state ;; delete files whose state has been set to nil @@ -750,6 +750,7 @@ Return the list of files that haven't been handled." (let (unmerged-files) (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t) (push (match-string 1) unmerged-files)) + (setq unmerged-files (nreverse unmerged-files)) ;; assume it is sorted already (git-set-filenames-state status unmerged-files 'unmerged)))) (defun git-get-exclude-files () @@ -770,17 +771,18 @@ Return the list of files that haven't been handled." (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files))))) (defun git-update-status-files (&optional files mark-files) - "Update the status of FILES from the index." + "Update the status of FILES from the index. +The FILES list must be sorted." (unless git-status (error "Not in git-status buffer.")) ;; set the needs-update flag on existing files - (if (setq files (sort files #'string-lessp)) + (if files (git-status-filenames-map git-status (lambda (info) (setf (git-fileinfo->needs-update info) t)) files) (ewoc-map (lambda (info) (setf (git-fileinfo->needs-update info) t) nil) git-status) (git-call-process nil "update-index" "--refresh") (when git-show-uptodate (git-run-ls-files-cached git-status nil 'uptodate))) - (let* ((remaining-files + (let ((remaining-files (if (git-empty-db-p) ; we need some special handling for an empty db (git-run-ls-files-cached git-status files 'added) (git-run-diff-index git-status files)))) @@ -825,13 +827,13 @@ Return the list of files that haven't been handled." (list (ewoc-data (ewoc-locate git-status))))) (defun git-marked-files-state (&rest states) - "Return marked files that are in the specified states." + "Return a sorted list of marked files that are in the specified states." (let ((files (git-marked-files)) result) (dolist (info files) (when (memq (git-fileinfo->state info) states) (push info result))) - result)) + (nreverse result))) (defun git-refresh-files () "Refresh all files that need it and clear the needs-refresh flag." @@ -1101,13 +1103,14 @@ Return the list of files that haven't been handled." (or (not added) (apply 'git-call-process-display-error "update-index" "--force-remove" "--" added)) (or (not modified) - (apply 'git-call-process-display-error "checkout" "HEAD" modified))))) - (git-update-status-files (append added modified)) + (apply 'git-call-process-display-error "checkout" "HEAD" modified)))) + (names (git-get-filenames files))) + (git-update-status-files names) (when ok (dolist (file modified) (let ((buffer (get-file-buffer file))) (when buffer (with-current-buffer buffer (revert-buffer t t t))))) - (git-success-message "Reverted" (git-get-filenames files))))))) + (git-success-message "Reverted" names)))))) (defun git-resolve-file () "Resolve conflicts in marked file(s)." @@ -1365,14 +1368,14 @@ Return the list of files that haven't been handled." (mapconcat #'identity msg "\n")))) (defun git-get-commit-files (commit) - "Retrieve the list of files modified by COMMIT." + "Retrieve a sorted list of files modified by COMMIT." (let (files) (with-temp-buffer (git-call-process t "diff-tree" "-m" "-r" "-z" "--name-only" "--no-commit-id" "--root" commit) (goto-char (point-min)) (while (re-search-forward "\\([^\0]*\\)\0" nil t 1) (push (match-string 1) files))) - files)) + (sort files #'string-lessp))) (defun git-read-commit-name (prompt &optional default) "Ask for a commit name, with completion for local branch, remote branch and tag." -- cgit v1.2.3 From 5b4e44104ea9f933e461ed7d3ce4a05a00d95dbb Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 16 Feb 2009 11:40:08 +0100 Subject: git.el: Improve the confirmation message on remove and revert. If there's only one file, print its name instead of just "1 file". Signed-off-by: Alexandre Julliard --- contrib/emacs/git.el | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index c7d15eb4dc..eace9c18eb 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -1068,7 +1068,9 @@ The FILES list must be sorted." (unless files (push (file-relative-name (read-file-name "File to remove: " nil nil t)) files)) (if (yes-or-no-p - (format "Remove %d file%s? " (length files) (if (> (length files) 1) "s" ""))) + (if (cdr files) + (format "Remove %d files? " (length files)) + (format "Remove %s? " (car files)))) (progn (dolist (name files) (ignore-errors @@ -1087,7 +1089,9 @@ The FILES list must be sorted." added modified) (when (and files (yes-or-no-p - (format "Revert %d file%s? " (length files) (if (> (length files) 1) "s" "")))) + (if (cdr files) + (format "Revert %d files? " (length files)) + (format "Revert %s? " (git-fileinfo->name (car files)))))) (dolist (info files) (case (git-fileinfo->state info) ('added (push (git-fileinfo->name info) added)) -- cgit v1.2.3 From 6f3c504b54e93772e875b6210fe69ecbad978e78 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 16 Feb 2009 11:40:29 +0100 Subject: Add a README in the contrib/emacs directory. Signed-off-by: Alexandre Julliard --- contrib/emacs/README | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 contrib/emacs/README (limited to 'contrib') diff --git a/contrib/emacs/README b/contrib/emacs/README new file mode 100644 index 0000000000..82368bdbff --- /dev/null +++ b/contrib/emacs/README @@ -0,0 +1,39 @@ +This directory contains various modules for Emacs support. + +To make the modules available to Emacs, you should add this directory +to your load-path, and then require the modules you want. This can be +done by adding to your .emacs something like this: + + (add-to-list 'load-path ".../git/contrib/emacs") + (require 'git) + (require 'git-blame) + + +The following modules are available: + +* git.el: + + Status manager that displays the state of all the files of the + project, and provides easy access to the most frequently used git + commands. The user interface is as far as possible compatible with + the pcl-cvs mode. It can be started with `M-x git-status'. + +* git-blame.el: + + Emacs implementation of incremental git-blame. When you turn it on + while viewing a file, the editor buffer will be updated by setting + the background of individual lines to a color that reflects which + commit it comes from. And when you move around the buffer, a + one-line summary will be shown in the echo area. + +* vc-git.el: + + This file used to contain the VC-mode backend for git, but it is no + longer distributed with git. It is now maintained as part of Emacs + and included in standard Emacs distributions starting from version + 22.2. + + If you have an earlier Emacs version, upgrading to Emacs 22 is + recommended, since the VC mode in older Emacs is not generic enough + to be able to support git in a reasonable manner, and no attempt has + been made to backport vc-git.el. -- cgit v1.2.3 From f50edca56c40cbfe48734eacd5d79416ba3649eb Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Sat, 21 Feb 2009 15:48:43 +0100 Subject: Add bare repository indicator for __git_ps1 Prefixes the branch name with "BARE:" if you're in a bare repository. Signed-off-by: Marius Storm-Olsen Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6e8c5b91ac..a61d852a14 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -135,11 +135,17 @@ __git_ps1 () fi fi + local c + + if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then + c="BARE:" + fi + if [ -n "$b" ]; then if [ -n "${1-}" ]; then - printf "$1" "${b##refs/heads/}$w$i$r" + printf "$1" "$c${b##refs/heads/}$w$i$r" else - printf " (%s)" "${b##refs/heads/}$w$i$r" + printf " (%s)" "$c${b##refs/heads/}$w$i$r" fi fi fi -- cgit v1.2.3 From b4b0ba06f8748348039d56ffa5890590dd9776ee Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Wed, 18 Feb 2009 13:12:14 -0500 Subject: git-p4: avoid syncing duplicate changes When a particular changeset affects multiple depot paths, it will appear multiple times in the output of "p4 changes". Filter out the duplicates to avoid the extra empty commits that this otherwise would create. Signed-off-by: Pete Wyckoff Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index a85a7b2a58..3832f60225 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -442,13 +442,14 @@ def p4ChangesForPaths(depotPaths, changeRange): output = p4_read_pipe_lines("changes " + ' '.join (["%s...%s" % (p, changeRange) for p in depotPaths])) - changes = [] + changes = {} for line in output: - changeNum = line.split(" ")[1] - changes.append(int(changeNum)) + changeNum = int(line.split(" ")[1]) + changes[changeNum] = True - changes.sort() - return changes + changelist = changes.keys() + changelist.sort() + return changelist class Command: def __init__(self): -- cgit v1.2.3 From 3ca936422212370481850d67cab80b1e517b2d80 Mon Sep 17 00:00:00 2001 From: Abhijit Menon-Sen Date: Wed, 25 Feb 2009 08:33:14 +0530 Subject: Convert git-* invocations to "git *" in the svnimport example. After these changes, git-svnimport worked fine for me. Signed-off-by: Abhijit Menon-Sen Signed-off-by: Junio C Hamano --- contrib/examples/git-svnimport.perl | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-svnimport.perl b/contrib/examples/git-svnimport.perl index a13bb6afec..4576c4a862 100755 --- a/contrib/examples/git-svnimport.perl +++ b/contrib/examples/git-svnimport.perl @@ -287,9 +287,9 @@ my $last_rev = ""; my $last_branch; my $current_rev = $opt_s || 1; unless(-d $git_dir) { - system("git-init"); + system("git init"); die "Cannot init the GIT db at $git_tree: $?\n" if $?; - system("git-read-tree"); + system("git read-tree"); die "Cannot init an empty tree: $?\n" if $?; $last_branch = $opt_o; @@ -303,7 +303,7 @@ unless(-d $git_dir) { -f "$git_dir/svn2git" or die "'$git_dir/svn2git' does not exist.\n". "You need that file for incremental imports.\n"; - open(F, "git-symbolic-ref HEAD |") or + open(F, "git symbolic-ref HEAD |") or die "Cannot run git-symbolic-ref: $!\n"; chomp ($last_branch = ); $last_branch = basename($last_branch); @@ -331,7 +331,7 @@ EOM "$git_dir/refs/heads/$opt_o") == 0; # populate index - system('git-read-tree', $last_rev); + system('git', 'read-tree', $last_rev); die "read-tree failed: $?\n" if $?; # Get the last import timestamps @@ -399,7 +399,7 @@ sub get_file($$$) { my $pid = open(my $F, '-|'); die $! unless defined $pid; if (!$pid) { - exec("git-hash-object", "-w", $name) + exec("git", "hash-object", "-w", $name) or die "Cannot create object: $!\n"; } my $sha = <$F>; @@ -423,7 +423,7 @@ sub get_ignore($$$$$) { my $pid = open(my $F, '-|'); die $! unless defined $pid; if (!$pid) { - exec("git-hash-object", "-w", $name) + exec("git", "hash-object", "-w", $name) or die "Cannot create object: $!\n"; } my $sha = <$F>; @@ -547,7 +547,7 @@ sub copy_path($$$$$$$$) { my $pid = open my $f,'-|'; die $! unless defined $pid; if (!$pid) { - exec("git-ls-tree","-r","-z",$gitrev,$srcpath) + exec("git","ls-tree","-r","-z",$gitrev,$srcpath) or die $!; } local $/ = "\0"; @@ -634,7 +634,7 @@ sub commit { my $rev; if($revision > $opt_s and defined $parent) { - open(H,'-|',"git-rev-parse","--verify",$parent); + open(H,'-|',"git","rev-parse","--verify",$parent); $rev = ; close(H) or do { print STDERR "$revision: cannot find commit '$parent'!\n"; @@ -671,7 +671,7 @@ sub commit { unlink($git_index); } elsif ($rev ne $last_rev) { print "Switching from $last_rev to $rev ($branch)\n" if $opt_v; - system("git-read-tree", $rev); + system("git", "read-tree", $rev); die "read-tree failed for $rev: $?\n" if $?; $last_rev = $rev; } @@ -740,7 +740,7 @@ sub commit { my $pid = open my $F, "-|"; die "$!" unless defined $pid; if (!$pid) { - exec("git-ls-files", "-z", @o1) or die $!; + exec("git", "ls-files", "-z", @o1) or die $!; } @o1 = (); local $/ = "\0"; @@ -758,7 +758,7 @@ sub commit { @o2 = @o1; @o1 = (); } - system("git-update-index","--force-remove","--",@o2); + system("git","update-index","--force-remove","--",@o2); die "Cannot remove files: $?\n" if $?; } } @@ -770,7 +770,7 @@ sub commit { @n2 = @new; @new = (); } - system("git-update-index","--add", + system("git","update-index","--add", (map { ('--cacheinfo', @$_) } @n2)); die "Cannot add files: $?\n" if $?; } @@ -778,7 +778,7 @@ sub commit { my $pid = open(C,"-|"); die "Cannot fork: $!" unless defined $pid; unless($pid) { - exec("git-write-tree"); + exec("git","write-tree"); die "Cannot exec git-write-tree: $!\n"; } chomp(my $tree = ); @@ -830,7 +830,7 @@ sub commit { "GIT_COMMITTER_NAME=$committer_name", "GIT_COMMITTER_EMAIL=$committer_email", "GIT_COMMITTER_DATE=".strftime("+0000 %Y-%m-%d %H:%M:%S",gmtime($date)), - "git-commit-tree", $tree,@par); + "git", "commit-tree", $tree,@par); die "Cannot exec git-commit-tree: $!\n"; } $pw->writer(); @@ -874,7 +874,7 @@ sub commit { $dest =~ tr/_/\./ if $opt_u; - system('git-tag', '-f', $dest, $cid) == 0 + system('git', 'tag', '-f', $dest, $cid) == 0 or die "Cannot create tag $dest: $!\n"; print "Created tag '$dest' on '$branch'\n" if $opt_v; @@ -937,7 +937,7 @@ while ($to_rev < $opt_l) { my $pid = fork(); die "Fork: $!\n" unless defined $pid; unless($pid) { - exec("git-repack", "-d") + exec("git", "repack", "-d") or die "Cannot repack: $!\n"; } waitpid($pid, 0); @@ -958,7 +958,7 @@ if($orig_branch) { system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") if $forward_master; unless ($opt_i) { - system('git-read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD'); + system('git', 'read-tree', '-m', '-u', 'SVN2GIT_HEAD', 'HEAD'); die "read-tree failed: $?\n" if $?; } } else { @@ -966,7 +966,7 @@ if($orig_branch) { print "DONE; creating $orig_branch branch\n" if $opt_v and (not defined $opt_l or $opt_l > 0); system("cp","$git_dir/refs/heads/$opt_o","$git_dir/refs/heads/master") unless -f "$git_dir/refs/heads/master"; - system('git-update-ref', 'HEAD', "$orig_branch"); + system('git', 'update-ref', 'HEAD', "$orig_branch"); unless ($opt_i) { system('git checkout'); die "checkout failed: $?\n" if $?; -- cgit v1.2.3 From ddb6d010231432ba75cf109aa7cd282912c88d2d Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Sat, 21 Feb 2009 15:48:43 +0100 Subject: Fixup: Add bare repository indicator for __git_ps1 Signed-off-by: Marius Storm-Olsen Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a61d852a14..dd393cd004 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -117,9 +117,14 @@ __git_ps1 () local w local i + local c if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then - b="GIT_DIR!" + if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then + c="BARE:" + else + b="GIT_DIR!" + fi elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then @@ -135,12 +140,6 @@ __git_ps1 () fi fi - local c - - if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then - c="BARE:" - fi - if [ -n "$b" ]; then if [ -n "${1-}" ]; then printf "$1" "$c${b##refs/heads/}$w$i$r" -- cgit v1.2.3 From 3dbe1165e9facec3497b3da744b832788a47957e Mon Sep 17 00:00:00 2001 From: Michael J Gruber Date: Wed, 25 Feb 2009 15:05:17 +0100 Subject: Fix typo in contrib/examples/git-svnimport.txt Signed-off-by: Michael J Gruber Signed-off-by: Junio C Hamano --- contrib/examples/git-svnimport.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/examples/git-svnimport.txt b/contrib/examples/git-svnimport.txt index 71aad8b45b..3bb871e42f 100644 --- a/contrib/examples/git-svnimport.txt +++ b/contrib/examples/git-svnimport.txt @@ -114,9 +114,9 @@ due to SVN memory leaks. (These have been worked around.) -R :: Specify how often git repository should be repacked. + -The default value is 1000. git-svnimport will do import in chunks of 1000 -revisions, after each chunk git repository will be repacked. To disable -this behavior specify some big value here which is mote than number of +The default value is 1000. git-svnimport will do imports in chunks of 1000 +revisions, after each chunk the git repository will be repacked. To disable +this behavior specify some large value here which is greater than the number of revisions to import. -P :: -- cgit v1.2.3 From 72de29c24f50dccc5f045a7756bb0b47e34a7a8e Mon Sep 17 00:00:00 2001 From: Teemu Likonen Date: Tue, 24 Feb 2009 15:33:29 +0200 Subject: bash completion: add --format= and --oneline options for "git log" We also add --format= completion for "git show". Signed-off-by: Teemu Likonen Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0a3092f646..31608cb79f 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1014,6 +1014,11 @@ _git_log () " "" "${cur##--pretty=}" return ;; + --format=*) + __gitcomp "$__git_log_pretty_formats + " "" "${cur##--format=}" + return + ;; --date=*) __gitcomp " relative iso8601 rfc2822 short local default @@ -1029,7 +1034,7 @@ _git_log () --follow --abbrev-commit --abbrev= --relative-date --date= - --pretty= + --pretty= --format= --oneline --cherry-pick --graph --decorate @@ -1541,8 +1546,13 @@ _git_show () " "" "${cur##--pretty=}" return ;; + --format=*) + __gitcomp "$__git_log_pretty_formats + " "" "${cur##--format=}" + return + ;; --*) - __gitcomp "--pretty= + __gitcomp "--pretty= --format= $__git_diff_common_options " return -- cgit v1.2.3 From bc14fac825d9728c311aaa9d0aecf4960d4a3103 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Wed, 25 Feb 2009 03:32:25 -0500 Subject: builtin-remote: add set-head subcommand Provide a porcelain command for setting and deleting $GIT_DIR/remotes//HEAD. While we're at it, document what $GIT_DIR/remotes//HEAD is all about. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0a3092f646..15b938b902 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1443,7 +1443,7 @@ _git_config () _git_remote () { - local subcommands="add rename rm show prune update" + local subcommands="add rename rm show prune update set-head" local subcommand="$(__git_find_subcommand "$subcommands")" if [ -z "$subcommand" ]; then __gitcomp "$subcommands" -- cgit v1.2.3 From 52d5c3b5b22b6a672ace19f631768a63bb6a2250 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Thu, 5 Mar 2009 23:39:31 -0500 Subject: bash completion: fix completion issues with fetch, pull, and push Sverre Rabbelier noticed a completion issue with push: $ git push ori git push origin $ git push -f ori git push -f origin/ Markus Heidelberg pointed out that the issue extends to fetch and pull. The reason is that the current code naively assumes that if COMP_CWORD=2, it should complete a remote name, otherwise it should complete a refspec. This assumption fails if there are any --options. This patch fixes that issue by instead scanning COMP_CWORDS to see if the remote has been completed yet (we now assume the first non-dashed argument is the remote). The new logic is factored into a function, shared by fetch, pull, and push. The new function also properly handles '.' as the remote. Signed-off-by: Jay Soffian Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 109 ++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 49 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index f234c34304..e8c4be2e81 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -387,6 +387,63 @@ __git_complete_revlist () esac } +__git_complete_remote_or_refspec () +{ + local cmd="${COMP_WORDS[1]}" + local cur="${COMP_WORDS[COMP_CWORD]}" + local i c=2 remote="" pfx="" lhs=1 + while [ $c -lt $COMP_CWORD ]; do + i="${COMP_WORDS[c]}" + case "$i" in + -*) ;; + *) remote="$i"; break ;; + esac + c=$((++c)) + done + if [ -z "$remote" ]; then + __gitcomp "$(__git_remotes)" + return + fi + [ "$remote" = "." ] && remote= + case "$cur" in + *:*) + case "$COMP_WORDBREAKS" in + *:*) : great ;; + *) pfx="${cur%%:*}:" ;; + esac + cur="${cur#*:}" + lhs=0 + ;; + +*) + pfx="+" + cur="${cur#+}" + ;; + esac + case "$cmd" in + fetch) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur" + else + __gitcomp "$(__git_refs)" "$pfx" "$cur" + fi + ;; + pull) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur" + else + __gitcomp "$(__git_refs)" "$pfx" "$cur" + fi + ;; + push) + if [ $lhs = 1 ]; then + __gitcomp "$(__git_refs)" "$pfx" "$cur" + else + __gitcomp "$(__git_refs "$remote")" "$pfx" "$cur" + fi + ;; + esac +} + __git_all_commands () { if [ -n "${__git_all_commandlist-}" ]; then @@ -832,25 +889,7 @@ _git_diff () _git_fetch () { - local cur="${COMP_WORDS[COMP_CWORD]}" - - if [ "$COMP_CWORD" = 2 ]; then - __gitcomp "$(__git_remotes)" - else - case "$cur" in - *:*) - local pfx="" - case "$COMP_WORDBREAKS" in - *:*) : great ;; - *) pfx="${cur%%:*}:" ;; - esac - __gitcomp "$(__git_refs)" "$pfx" "${cur#*:}" - ;; - *) - __gitcomp "$(__git_refs2 "${COMP_WORDS[2]}")" - ;; - esac - fi + __git_complete_remote_or_refspec } _git_format_patch () @@ -1120,40 +1159,12 @@ _git_name_rev () _git_pull () { - local cur="${COMP_WORDS[COMP_CWORD]}" - - if [ "$COMP_CWORD" = 2 ]; then - __gitcomp "$(__git_remotes)" - else - __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" - fi + __git_complete_remote_or_refspec } _git_push () { - local cur="${COMP_WORDS[COMP_CWORD]}" - - if [ "$COMP_CWORD" = 2 ]; then - __gitcomp "$(__git_remotes)" - else - case "$cur" in - *:*) - local pfx="" - case "$COMP_WORDBREAKS" in - *:*) : great ;; - *) pfx="${cur%%:*}:" ;; - esac - - __gitcomp "$(__git_refs "${COMP_WORDS[2]}")" "$pfx" "${cur#*:}" - ;; - +*) - __gitcomp "$(__git_refs)" + "${cur#+}" - ;; - *) - __gitcomp "$(__git_refs)" - ;; - esac - fi + __git_complete_remote_or_refspec } _git_rebase () -- cgit v1.2.3 From 3c7b480a1cf6e1a1c73b4edde5d8cf0ac0c8111c Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Fri, 6 Mar 2009 11:30:44 -0500 Subject: bash completion: refactor --strategy completion The code to complete --strategy was duplicated between _git_rebase and _git_merge, and is about to gain a third caller (_git_pull). This patch factors it into its own function. Signed-off-by: Jay Soffian Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 38 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 18 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e8c4be2e81..056e43e4ad 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -444,6 +444,23 @@ __git_complete_remote_or_refspec () esac } +__git_complete_strategy () +{ + case "${COMP_WORDS[COMP_CWORD-1]}" in + -s|--strategy) + __gitcomp "$(__git_merge_strategies)" + return 0 + esac + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --strategy=*) + __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" + return 0 + ;; + esac + return 1 +} + __git_all_commands () { if [ -n "${__git_all_commandlist-}" ]; then @@ -1095,17 +1112,10 @@ _git_log () _git_merge () { + __git_complete_strategy && return + local cur="${COMP_WORDS[COMP_CWORD]}" - case "${COMP_WORDS[COMP_CWORD-1]}" in - -s|--strategy) - __gitcomp "$(__git_merge_strategies)" - return - esac case "$cur" in - --strategy=*) - __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" - return - ;; --*) __gitcomp " --no-commit --no-stat --log --no-log --squash --strategy @@ -1174,16 +1184,8 @@ _git_rebase () __gitcomp "--continue --skip --abort" return fi - case "${COMP_WORDS[COMP_CWORD-1]}" in - -s|--strategy) - __gitcomp "$(__git_merge_strategies)" - return - esac + __git_complete_strategy && return case "$cur" in - --strategy=*) - __gitcomp "$(__git_merge_strategies)" "" "${cur##--strategy=}" - return - ;; --*) __gitcomp "--onto --merge --strategy --interactive" return -- cgit v1.2.3 From 0a4e14727f53ba2e8263622ba5de917b2f9d1575 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Thu, 5 Mar 2009 23:39:33 -0500 Subject: bash completion: teach fetch, pull, and push to complete their options fetch, pull, and push didn't know their options. They do now. merge's options are factored into a variable so they can be shared between _git_merge and _git_pull Signed-off-by: Jay Soffian Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 61 +++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 056e43e4ad..271b911f7a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -391,10 +391,11 @@ __git_complete_remote_or_refspec () { local cmd="${COMP_WORDS[1]}" local cur="${COMP_WORDS[COMP_CWORD]}" - local i c=2 remote="" pfx="" lhs=1 + local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0 while [ $c -lt $COMP_CWORD ]; do i="${COMP_WORDS[c]}" case "$i" in + --all|--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;; -*) ;; *) remote="$i"; break ;; esac @@ -404,6 +405,10 @@ __git_complete_remote_or_refspec () __gitcomp "$(__git_remotes)" return fi + if [ $no_complete_refspec = 1 ]; then + COMPREPLY=() + return + fi [ "$remote" = "." ] && remote= case "$cur" in *:*) @@ -904,8 +909,20 @@ _git_diff () __git_complete_file } +__git_fetch_options=" + --quiet --verbose --append --upload-pack --force --keep --depth= + --tags --no-tags +" + _git_fetch () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp "$__git_fetch_options" + return + ;; + esac __git_complete_remote_or_refspec } @@ -1110,6 +1127,11 @@ _git_log () __git_complete_revlist } +__git_merge_options=" + --no-commit --no-stat --log --no-log --squash --strategy + --commit --stat --no-squash --ff --no-ff +" + _git_merge () { __git_complete_strategy && return @@ -1117,10 +1139,7 @@ _git_merge () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp " - --no-commit --no-stat --log --no-log --squash --strategy - --commit --stat --no-squash --ff --no-ff - " + __gitcomp "$__git_merge_options" return esac __gitcomp "$(__git_refs)" @@ -1169,11 +1188,43 @@ _git_name_rev () _git_pull () { + __git_complete_strategy && return + + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --rebase --no-rebase + $__git_merge_options + $__git_fetch_options + " + return + ;; + esac __git_complete_remote_or_refspec } _git_push () { + local cur="${COMP_WORDS[COMP_CWORD]}" + case "${COMP_WORDS[COMP_CWORD-1]}" in + --repo) + __gitcomp "$(__git_remotes)" + return + esac + case "$cur" in + --repo=*) + __gitcomp "$(__git_remotes)" "" "${cur##--repo=}" + return + ;; + --*) + __gitcomp " + --all --mirror --tags --dry-run --force --verbose + --receive-pack= --repo= + " + return + ;; + esac __git_complete_remote_or_refspec } -- cgit v1.2.3 From 3b167396b416541f7559f3141392d56b93ea049c Mon Sep 17 00:00:00 2001 From: Pete Wyckoff Date: Fri, 27 Feb 2009 10:53:59 -0800 Subject: git-p4: remove tabs from usermap file Some users have tabs in their names, oddly enough. This causes problems when loading the usercache from disk, as split separates the fields on the wrong tabs. When fast-import's parse_ident() tries to parse the committer field, it is unhappy about the unbalanced <..> angle brackets. It is easy enough to convert the tabs to single spaces. Signed-off-by: Pete Wyckoff Acked-by: Simon Hausmann Signed-off-by: Junio C Hamano --- contrib/fast-import/git-p4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4 index 3832f60225..342529db30 100755 --- a/contrib/fast-import/git-p4 +++ b/contrib/fast-import/git-p4 @@ -1142,7 +1142,7 @@ class P4Sync(Command): s = '' for (key, val) in self.users.items(): - s += "%s\t%s\n" % (key, val) + s += "%s\t%s\n" % (key.expandtabs(1), val.expandtabs(1)) open(self.getUserCacheFilename(), "wb").write(s) self.userMapFromPerforceServer = True -- cgit v1.2.3 From 2464456a6ac9216d59d9e2cf0d86fee072f63cf8 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Mon, 9 Mar 2009 02:12:36 -0700 Subject: contrib/difftool: use a separate config namespace for difftool commands Some users have different mergetool and difftool settings, so teach difftool to read config vars from the difftool.* namespace. This allows having distinct configurations for the diff and merge scenarios. We don't want to force existing users to set new values for no reason so difftool falls back to existing mergetool config variables when the difftool equivalents are not defined. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool | 6 +++--- contrib/difftool/git-difftool-helper | 19 ++++++++++++++----- contrib/difftool/git-difftool.txt | 30 +++++++++++++++--------------- 3 files changed, 32 insertions(+), 23 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool index 0cda3d2eea..0deda3a0e4 100755 --- a/contrib/difftool/git-difftool +++ b/contrib/difftool/git-difftool @@ -4,7 +4,7 @@ # This is a wrapper around the GIT_EXTERNAL_DIFF-compatible # git-difftool-helper script. This script exports # GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and -# GIT_DIFFTOOL_NO_PROMPT and GIT_MERGE_TOOL for use by git-difftool-helper. +# GIT_DIFFTOOL_NO_PROMPT and GIT_DIFF_TOOL for use by git-difftool-helper. # Any arguments that are unknown to this script are forwarded to 'git diff'. use strict; @@ -49,12 +49,12 @@ sub generate_command } if ($arg eq '-t' or $arg eq '--tool') { usage() if $#ARGV <= $idx; - $ENV{GIT_MERGE_TOOL} = $ARGV[$idx + 1]; + $ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1]; $skip_next = 1; next; } if ($arg =~ /^--tool=/) { - $ENV{GIT_MERGE_TOOL} = substr($arg, 7); + $ENV{GIT_DIFF_TOOL} = substr($arg, 7); next; } if ($arg eq '--no-prompt') { diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index db3af6a833..9c0a13452a 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -128,8 +128,10 @@ launch_merge_tool () { cleanup_temp_files } -# Verifies that mergetool..cmd exists +# Verifies that (difftool|mergetool)..cmd exists valid_custom_tool() { + merge_tool_cmd="$(git config difftool.$1.cmd)" + test -z "$merge_tool_cmd" && merge_tool_cmd="$(git config mergetool.$1.cmd)" test -n "$merge_tool_cmd" } @@ -150,8 +152,11 @@ valid_tool() { } # Sets up the merge_tool_path variable. -# This handles the mergetool..path configuration. +# This handles the difftool..path configuration. +# This also falls back to mergetool defaults. init_merge_tool_path() { + merge_tool_path=$(git config difftool."$1".path) + test -z "$merge_tool_path" && merge_tool_path=$(git config mergetool."$1".path) if test -z "$merge_tool_path"; then case "$1" in @@ -165,15 +170,19 @@ init_merge_tool_path() { fi } -# Allow the GIT_MERGE_TOOL variable to provide a default value +# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL" +test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL" -# If not merge tool was specified then use the merge.tool +# If merge tool was not specified then use the diff.tool # configuration variable. If that's invalid then reset merge_tool. +# Fallback to merge.tool. if test -z "$merge_tool"; then + merge_tool=$(git config diff.tool) + test -z "$merge_tool" && merge_tool=$(git config merge.tool) if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then - echo >&2 "git config option merge.tool set to unknown tool: $merge_tool" + echo >&2 "git config option diff.tool set to unknown tool: $merge_tool" echo >&2 "Resetting to default..." unset merge_tool fi diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index 6e2610cda6..2b7bc03ec3 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -32,23 +32,23 @@ OPTIONS vimdiff, gvimdiff, ecmerge, and opendiff + If a merge resolution program is not specified, 'git-difftool' -will use the configuration variable `merge.tool`. If the -configuration variable `merge.tool` is not set, 'git difftool' +will use the configuration variable `diff.tool`. If the +configuration variable `diff.tool` is not set, 'git-difftool' will pick a suitable default. + You can explicitly provide a full path to the tool by setting the -configuration variable `mergetool..path`. For example, you +configuration variable `difftool..path`. For example, you can configure the absolute path to kdiff3 by setting -`mergetool.kdiff3.path`. Otherwise, 'git-difftool' assumes the +`difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the tool is available in PATH. + Instead of running one of the known merge tool programs, 'git-difftool' can be customized to run an alternative program by specifying the command line to invoke in a configuration -variable `mergetool..cmd`. +variable `difftool..cmd`. + When 'git-difftool' is invoked with this tool (either through the -`-t` or `--tool` option or the `merge.tool` configuration variable) +`-t` or `--tool` option or the `diff.tool` configuration variable) the configured command line will be invoked with the following variables available: `$LOCAL` is set to the name of the temporary file containing the contents of the diff pre-image and `$REMOTE` @@ -61,24 +61,24 @@ with custom merge tool commands and has the same value as `$LOCAL`. CONFIG VARIABLES ---------------- -merge.tool:: - The default merge tool to use. -+ -See the `--tool=` option above for more details. +'git-difftool' falls back to 'git-mergetool' config variables when the +difftool equivalents have not been defined. -merge.keepBackup:: - The original, unedited file content can be saved to a file with - a `.orig` extension. Defaults to `true` (i.e. keep the backup files). +diff.tool:: + The default merge tool to use. -mergetool..path:: +difftool..path:: Override the path for the given tool. This is useful in case your tool is not in the PATH. -mergetool..cmd:: +difftool..cmd:: Specify the command to invoke the specified merge tool. + See the `--tool=` option above for more details. +merge.keepBackup:: + The original, unedited file content can be saved to a file with + a `.orig` extension. Defaults to `true` (i.e. keep the backup files). SEE ALSO -------- -- cgit v1.2.3 From 6872f606d9bc9a0ab0b3252bd4175af7732b6135 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Fri, 20 Mar 2009 10:57:50 +0100 Subject: import-tars: separate author from committer The import-tars script is typically employed to (re)create the past history of a project from stored tars. Although assigning authorship in these cases can be a somewhat arbitrary process, it makes sense to set the author to whoever created the tars in the first place (if it's known), and (s)he can in general be different from the committer (whoever is running the script). Implement this by having separate author and committer data, making them settable from the usual GIT_* environment variables. Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/fast-import/import-tars.perl | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 23aeb257b9..6309d146e7 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -14,13 +14,18 @@ die "usage: import-tars *.tar.{gz,bz2,Z}\n" unless @ARGV; my $branch_name = 'import-tars'; my $branch_ref = "refs/heads/$branch_name"; -my $committer_name = 'T Ar Creator'; -my $committer_email = 'tar@example.com'; +my $author_name = $ENV{'GIT_AUTHOR_NAME'} || 'T Ar Creator'; +my $author_email = $ENV{'GIT_AUTHOR_EMAIL'} || 'tar@example.com'; +my $committer_name = $ENV{'GIT_COMMITTER_NAME'} || `git config --get user.name`; +my $committer_email = $ENV{'GIT_COMMITTER_EMAIL'} || `git config --get user.email`; + +chomp($committer_name, $committer_email); open(FI, '|-', 'git', 'fast-import', '--quiet') or die "Unable to start git fast-import: $!\n"; foreach my $tar_file (@ARGV) { + my $commit_time = time; $tar_file =~ m,([^/]+)$,; my $tar_name = $1; @@ -39,7 +44,7 @@ foreach my $tar_file (@ARGV) die "Unrecognized compression format: $tar_file\n"; } - my $commit_time = 0; + my $author_time = 0; my $next_mark = 1; my $have_top_dir = 1; my ($top_dir, %files); @@ -92,7 +97,7 @@ foreach my $tar_file (@ARGV) } $files{$path} = [$next_mark++, $mode]; - $commit_time = $mtime if $mtime > $commit_time; + $author_time = $mtime if $mtime > $author_time; $path =~ m,^([^/]+)/,; $top_dir = $1 unless $top_dir; $have_top_dir = 0 if $top_dir ne $1; @@ -100,6 +105,7 @@ foreach my $tar_file (@ARGV) print FI < $author_time +0000 committer $committer_name <$committer_email> $commit_time +0000 data < $commit_time +0000 +tagger $author_name <$author_email> $author_time +0000 data < Date: Sat, 21 Mar 2009 16:29:27 -0700 Subject: Add --staged to bash completion for git diff The --staged option (synonym for --cached) isn't listed in the completion choices for git diff. This tiny patch adds it. Trivially-Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index ed235f7596..6bc32df178 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -899,7 +899,7 @@ _git_diff () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--cached --pickaxe-all --pickaxe-regex + __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex --base --ours --theirs $__git_diff_common_options " -- cgit v1.2.3 From 4bca86367bff80ad3c04e282a1aa9ed66db26aa1 Mon Sep 17 00:00:00 2001 From: Arto Jonsson Date: Sun, 22 Mar 2009 20:49:07 +0200 Subject: bash completion: add options for 'git fsck' Signed-off-by: Arto Jonsson Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 6bc32df178..10e36a7b0d 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -952,6 +952,21 @@ _git_format_patch () __git_complete_revlist } +_git_fsck () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --*) + __gitcomp " + --tags --root --unreachable --cache --no-reflogs --full + --strict --verbose --lost-found + " + return + ;; + esac + COMPREPLY=() +} + _git_gc () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1880,6 +1895,7 @@ _git () diff) _git_diff ;; fetch) _git_fetch ;; format-patch) _git_format_patch ;; + fsck) _git_fsck ;; gc) _git_gc ;; grep) _git_grep ;; help) _git_help ;; -- cgit v1.2.3 From 77813151f983a77f5b5954fb7cb8094198db0567 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 23 Mar 2009 03:26:49 -0700 Subject: completion: add --annotate option to send-email Signed-off-by: Stephen Boyd Trivially-Acked-By: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 10e36a7b0d..8719242498 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1264,8 +1264,8 @@ _git_send_email () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --*) - __gitcomp "--bcc --cc --cc-cmd --chain-reply-to --compose - --dry-run --envelope-sender --from --identity + __gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to + --compose --dry-run --envelope-sender --from --identity --in-reply-to --no-chain-reply-to --no-signed-off-by-cc --no-suppress-from --no-thread --quiet --signed-off-by-cc --smtp-pass --smtp-server -- cgit v1.2.3 From 3f7df3a71a9cbab3c27d84f2410e7d39407f9013 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 23 Mar 2009 03:26:50 -0700 Subject: completion: add --cc and --no-attachment option to format-patch Signed-off-by: Stephen Boyd Trivially-Acked-By: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 8719242498..b96458f296 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -932,13 +932,13 @@ _git_format_patch () case "$cur" in --*) __gitcomp " - --stdout --attach --thread + --stdout --attach --no-attach --thread --output-directory --numbered --start-number --numbered-files --keep-subject --signoff - --in-reply-to= + --in-reply-to= --cc= --full-index --binary --not --all --cover-letter -- cgit v1.2.3 From e1d37937ac3c15898afdb6117349a30d0eae5e64 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 23 Mar 2009 03:26:51 -0700 Subject: completion: add --thread=deep/shallow to format-patch Signed-off-by: Stephen Boyd Trivially-Acked-By: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b96458f296..1c6b0e28ef 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -930,9 +930,15 @@ _git_format_patch () { local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in + --thread=*) + __gitcomp " + deep shallow + " "" "${cur##--thread=}" + return + ;; --*) __gitcomp " - --stdout --attach --no-attach --thread + --stdout --attach --no-attach --thread --thread= --output-directory --numbered --start-number --numbered-files -- cgit v1.2.3 From 63801da88d8638eb6cf26d6305a721ad3731e216 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Sun, 29 Mar 2009 22:42:27 +0200 Subject: import-zips: fix thinko Embarrassingly, the common prefix calculation did not work properly, due to a mistake in the assignment: instead of assigning the dirname of the current file name, the dirname of the current common prefix needs to be assigned to common prefix, when the current prefix does not match the current file name. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/fast-import/import-zips.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-zips.py b/contrib/fast-import/import-zips.py index c674fa2d1b..7051a83a59 100755 --- a/contrib/fast-import/import-zips.py +++ b/contrib/fast-import/import-zips.py @@ -44,7 +44,8 @@ for zipfile in argv[1:]: common_prefix = name[:name.rfind('/') + 1] else: while not name.startswith(common_prefix): - common_prefix = name[:name.rfind('/') + 1] + last_slash = common_prefix[:-1].rfind('/') + 1 + common_prefix = common_prefix[:last_slash] mark[name] = ':' + str(next_mark) next_mark += 1 -- cgit v1.2.3 From 67f1fe5f08d3f6146cf13f8a65ceeab1509581a8 Mon Sep 17 00:00:00 2001 From: Thomas Rast Date: Mon, 16 Feb 2009 17:34:57 +0100 Subject: bash completion: only show 'log --merge' if merging The gitk completion only shows --merge if MERGE_HEAD is present. Do it the same way for git-log completion. Signed-off-by: Thomas Rast Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 554a03ff4f..0bb74c05e5 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -937,6 +937,11 @@ _git_log () __git_has_doubledash && return local cur="${COMP_WORDS[COMP_CWORD]}" + local g="$(git rev-parse --git-dir 2>/dev/null)" + local merge="" + if [ -f $g/MERGE_HEAD ]; then + merge="--merge" + fi case "$cur" in --pretty=*) __gitcomp " @@ -968,7 +973,7 @@ _git_log () --decorate --diff-filter= --color-words --walk-reflogs --parents --children --full-history - --merge + $merge " return ;; -- cgit v1.2.3 From ba7906f2f4c332f814d270d2e16b0010516fc53e Mon Sep 17 00:00:00 2001 From: "Daniel Cheng (aka SDiZ)" Date: Mon, 30 Mar 2009 19:27:37 +0800 Subject: Fix bash completion in path with spaces Signed-off-by: Daniel Cheng (aka SDiZ) Trivially-acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0bb74c05e5..8fc01fb497 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -939,7 +939,7 @@ _git_log () local cur="${COMP_WORDS[COMP_CWORD]}" local g="$(git rev-parse --git-dir 2>/dev/null)" local merge="" - if [ -f $g/MERGE_HEAD ]; then + if [ -f "$g/MERGE_HEAD" ]; then merge="--merge" fi case "$cur" in @@ -1681,7 +1681,7 @@ _gitk () local cur="${COMP_WORDS[COMP_CWORD]}" local g="$(git rev-parse --git-dir 2>/dev/null)" local merge="" - if [ -f $g/MERGE_HEAD ]; then + if [ -f "$g/MERGE_HEAD" ]; then merge="--merge" fi case "$cur" in -- cgit v1.2.3 From 89a56bfbd3fd855cb0c2a381520e6255de41a55e Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Sun, 5 Apr 2009 04:15:16 +0200 Subject: add --html-path to get the location of installed HTML docs This can be used in GUIs to open installed HTML documentation in the browser. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 1 + 1 file changed, 1 insertion(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e72ce2428d..fdc5a24b27 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1870,6 +1870,7 @@ _git () --bare --version --exec-path + --html-path --work-tree= --help " -- cgit v1.2.3 From 43acdf243ee8a8fa876bdd6659026fe5ed2d4c24 Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Sun, 5 Apr 2009 12:33:38 -0400 Subject: bash completion: Update 'git am' options This adds --committer-date-is-author-date, --ignore-date, and --no-utf8 options. The --binary option is removed, as it was made a no-op by cb3a160. The option list is also sorted alphabetically. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e72ce2428d..d3d8203171 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -646,7 +646,8 @@ _git_am () ;; --*) __gitcomp " - --signoff --utf8 --binary --3way --interactive + --3way --committer-date-is-author-date --ignore-date + --interactive --keep --no-utf8 --signoff --utf8 --whitespace= " return -- cgit v1.2.3 From bad42732008cb0c1e77046d716e4446b1545d4d0 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Mon, 6 Apr 2009 01:31:17 -0700 Subject: git-mergetool/difftool: make (g)vimdiff workable under Windows Under Windows vimdiff and gvimdiff are not available as symbolic links, but as batch files vimdiff.bat and gvimdiff.bat. These files weren't found by 'type vimdiff' which led to the following error: The merge tool vimdiff is not available as 'vimdiff' Even if they were found, it wouldn't work to invoke these batch files from git-mergetool. To solve this, use vim and gvim (vim.exe and gvim.exe) and pass the -d command line switch over to them. Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index 9c0a13452a..e481913c91 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -86,11 +86,11 @@ launch_merge_tool () { ;; vimdiff) - "$merge_tool_path" -c "wincmd l" "$LOCAL" "$REMOTE" + "$merge_tool_path" -d -c "wincmd l" "$LOCAL" "$REMOTE" ;; gvimdiff) - "$merge_tool_path" -c "wincmd l" -f "$LOCAL" "$REMOTE" + "$merge_tool_path" -d -c "wincmd l" -f "$LOCAL" "$REMOTE" ;; xxdiff) @@ -160,6 +160,12 @@ init_merge_tool_path() { merge_tool_path=$(git config mergetool."$1".path) if test -z "$merge_tool_path"; then case "$1" in + vimdiff) + merge_tool_path=vim + ;; + gvimdiff) + merge_tool_path=gvim + ;; emerge) merge_tool_path=emacs ;; -- cgit v1.2.3 From 76ca653842057766773776bffc6a415b39d5a147 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Mon, 6 Apr 2009 01:31:19 -0700 Subject: difftool: remove merge options for opendiff, tkdiff, kdiff3 and xxdiff We shouldn't try to merge files when using difftool, so remove any merge-specific options. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index e481913c91..ef684b6f68 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -69,7 +69,7 @@ launch_merge_tool () { "$merge_tool_path" --auto \ --L1 "$basename (A)" \ --L2 "$basename (B)" \ - -o "$MERGED" "$LOCAL" "$REMOTE" \ + "$LOCAL" "$REMOTE" \ > /dev/null 2>&1 ;; @@ -78,7 +78,7 @@ launch_merge_tool () { ;; tkdiff) - "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE" + "$merge_tool_path" "$LOCAL" "$REMOTE" ;; meld) @@ -95,17 +95,13 @@ launch_merge_tool () { xxdiff) "$merge_tool_path" \ - -X \ - -R 'Accel.SaveAsMerged: "Ctrl-S"' \ -R 'Accel.Search: "Ctrl+F"' \ -R 'Accel.SearchForward: "Ctrl-G"' \ - --merged-file "$MERGED" \ "$LOCAL" "$REMOTE" ;; opendiff) - "$merge_tool_path" "$LOCAL" "$REMOTE" \ - -merge "$MERGED" | cat + "$merge_tool_path" "$LOCAL" "$REMOTE" | cat ;; ecmerge) -- cgit v1.2.3 From 2e8af7e42b15d4f2d573329ea2593a19f45f18d3 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Mon, 6 Apr 2009 01:31:20 -0700 Subject: difftool: remove the backup file feature Most users find the backup file feature annoying and there's no need for it since diff is supposed to be a read-only operation. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool-helper | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index ef684b6f68..e74a2747b6 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -9,31 +9,7 @@ # Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt. should_prompt () { - ! test -n "$GIT_DIFFTOOL_NO_PROMPT" -} - -# Should we keep the backup .orig file? -keep_backup_mode="$(git config --bool merge.keepBackup || echo true)" -keep_backup () { - test "$keep_backup_mode" = "true" -} - -# This function manages the backup .orig file. -# A backup $MERGED.orig file is created if changes are detected. -cleanup_temp_files () { - if test -n "$MERGED"; then - if keep_backup && test "$MERGED" -nt "$BACKUP"; then - test -f "$BACKUP" && mv -- "$BACKUP" "$MERGED.orig" - else - rm -f -- "$BACKUP" - fi - fi -} - -# This is called when users Ctrl-C out of git-difftool-helper -sigint_handler () { - cleanup_temp_files - exit 1 + test -z "$GIT_DIFFTOOL_NO_PROMPT" } # This function prepares temporary files and launches the appropriate @@ -47,12 +23,6 @@ launch_merge_tool () { LOCAL="$2" REMOTE="$3" BASE="$1" - ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')" - BACKUP="$MERGED.BACKUP.$ext" - - # Create and ensure that we clean up $BACKUP - test -f "$MERGED" && cp -- "$MERGED" "$BACKUP" - trap sigint_handler INT # $LOCAL and $REMOTE are temporary files so prompt # the user with the real $MERGED name before launching $merge_tool. @@ -120,8 +90,6 @@ launch_merge_tool () { fi ;; esac - - cleanup_temp_files } # Verifies that (difftool|mergetool)..cmd exists -- cgit v1.2.3 From 46ae156d6c8c48d521e4d858ed84d93259cfc61f Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Mon, 6 Apr 2009 01:31:21 -0700 Subject: difftool: use perl built-ins when testing for msys I don't even know what $COMSPEC means so let's be safe and use the same perly $^O test add--interactive uses. While we're at it, make git-difftool match the prevalent git-perl style. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool index 0deda3a0e4..207dd50f2c 100755 --- a/contrib/difftool/git-difftool +++ b/contrib/difftool/git-difftool @@ -33,7 +33,10 @@ sub setup_environment sub exe { my $exe = shift; - return defined $ENV{COMSPEC} ? "$exe.exe" : $exe; + if ($^O eq 'MSWin32' || $^O eq 'msys') { + return "$exe.exe"; + } + return $exe; } sub generate_command @@ -47,7 +50,7 @@ sub generate_command $skip_next = 0; next; } - if ($arg eq '-t' or $arg eq '--tool') { + if ($arg eq '-t' || $arg eq '--tool') { usage() if $#ARGV <= $idx; $ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1]; $skip_next = 1; -- cgit v1.2.3 From 8b7332221db8522fe23bf8cf25d058acea6b9142 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Tue, 7 Apr 2009 01:21:19 -0700 Subject: difftool: add a -y shortcut for --no-prompt This is another consistency cleanup to make git-difftool's options match git-mergetool. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool | 6 +++--- contrib/difftool/git-difftool.txt | 36 ++++++++++++++---------------------- 2 files changed, 17 insertions(+), 25 deletions(-) (limited to 'contrib') diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool index 207dd50f2c..8c160e5bb4 100755 --- a/contrib/difftool/git-difftool +++ b/contrib/difftool/git-difftool @@ -18,7 +18,7 @@ my $DIR = abs_path(dirname($0)); sub usage { print << 'USAGE'; -usage: git difftool [--tool=] [--no-prompt] ["git diff" options] +usage: git difftool [--tool=] [-y|--no-prompt] ["git diff" options] USAGE exit 1; } @@ -60,11 +60,11 @@ sub generate_command $ENV{GIT_DIFF_TOOL} = substr($arg, 7); next; } - if ($arg eq '--no-prompt') { + if ($arg eq '-y' || $arg eq '--no-prompt') { $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true'; next; } - if ($arg eq '-h' or $arg eq '--help') { + if ($arg eq '-h' || $arg eq '--help') { usage(); } push @command, $arg; diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index 2b7bc03ec3..a00e9431c5 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -3,35 +3,32 @@ git-difftool(1) NAME ---- -git-difftool - compare changes using common merge tools +git-difftool - Show changes using common diff tools SYNOPSIS -------- -'git difftool' [--tool=] [--no-prompt] ['git diff' options] +'git difftool' [--tool=] [-y|--no-prompt] [<'git diff' options>] DESCRIPTION ----------- 'git-difftool' is a git command that allows you to compare and edit files -between revisions using common merge tools. At its most basic level, -'git-difftool' does what 'git-mergetool' does but its use is for non-merge -situations such as when preparing commits or comparing changes against -the index. - -'git difftool' is a frontend to 'git diff' and accepts the same -arguments and options. - -See linkgit:git-diff[1] for the full list of supported options. +between revisions using common diff tools. 'git difftool' is a frontend +to 'git-diff' and accepts the same options and arguments. OPTIONS ------- +-y:: +--no-prompt:: + Do not prompt before launching a diff tool. + -t :: --tool=:: - Use the merge resolution program specified by . + Use the diff tool specified by . Valid merge tools are: kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff + -If a merge resolution program is not specified, 'git-difftool' +If a diff tool is not specified, 'git-difftool' will use the configuration variable `diff.tool`. If the configuration variable `diff.tool` is not set, 'git-difftool' will pick a suitable default. @@ -42,7 +39,7 @@ can configure the absolute path to kdiff3 by setting `difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the tool is available in PATH. + -Instead of running one of the known merge tool programs, +Instead of running one of the known diff tools, 'git-difftool' can be customized to run an alternative program by specifying the command line to invoke in a configuration variable `difftool..cmd`. @@ -56,8 +53,7 @@ is set to the name of the temporary file containing the contents of the diff post-image. `$BASE` is provided for compatibility with custom merge tool commands and has the same value as `$LOCAL`. ---no-prompt:: - Do not prompt before launching a diff tool. +See linkgit:git-diff[1] for the full list of supported options. CONFIG VARIABLES ---------------- @@ -65,21 +61,17 @@ CONFIG VARIABLES difftool equivalents have not been defined. diff.tool:: - The default merge tool to use. + The default diff tool to use. difftool..path:: Override the path for the given tool. This is useful in case your tool is not in the PATH. difftool..cmd:: - Specify the command to invoke the specified merge tool. + Specify the command to invoke the specified diff tool. + See the `--tool=` option above for more details. -merge.keepBackup:: - The original, unedited file content can be saved to a file with - a `.orig` extension. Defaults to `true` (i.e. keep the backup files). - SEE ALSO -------- linkgit:git-diff[1]:: -- cgit v1.2.3 From 1c0f3d224eff2ff8ca8442811edb5a6830adba1a Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Mon, 6 Apr 2009 01:31:23 -0700 Subject: difftool/mergetool: add diffuse as merge and diff tool This adds diffuse as a built-in merge tool. Signed-off-by: Sebastian Pipping Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 3 ++- contrib/difftool/git-difftool-helper | 10 ++++++---- contrib/difftool/git-difftool.txt | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d3d8203171..e099ed48ff 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1174,7 +1174,8 @@ _git_mergetool () --tool=*) __gitcomp " kdiff3 tkdiff meld xxdiff emerge - vimdiff gvimdiff ecmerge opendiff + vimdiff gvimdiff ecmerge diffuse + opendiff " "" "${cur##--tool=}" return ;; diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper index e74a2747b6..4b0daec5a7 100755 --- a/contrib/difftool/git-difftool-helper +++ b/contrib/difftool/git-difftool-helper @@ -1,7 +1,5 @@ #!/bin/sh # git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher. -# It supports kdiff3, kompare, tkdiff, xxdiff, meld, opendiff, -# emerge, ecmerge, vimdiff, gvimdiff, and custom user-configurable tools. # This script is typically launched by using the 'git difftool' # convenience command. # @@ -55,6 +53,10 @@ launch_merge_tool () { "$merge_tool_path" "$LOCAL" "$REMOTE" ;; + diffuse) + "$merge_tool_path" "$LOCAL" "$REMOTE" | cat + ;; + vimdiff) "$merge_tool_path" -d -c "wincmd l" "$LOCAL" "$REMOTE" ;; @@ -164,9 +166,9 @@ if test -z "$merge_tool"; then if test -n "$DISPLAY"; then # If gnome then prefer meld, otherwise, prefer kdiff3 or kompare if test -n "$GNOME_DESKTOP_SESSION_ID" ; then - merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff" + merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff diffuse" else - merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff" + merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff diffuse" fi fi if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt index a00e9431c5..af68466ebc 100644 --- a/contrib/difftool/git-difftool.txt +++ b/contrib/difftool/git-difftool.txt @@ -25,8 +25,8 @@ OPTIONS --tool=:: Use the diff tool specified by . Valid merge tools are: - kdiff3, kompare, tkdiff, meld, xxdiff, emerge, - vimdiff, gvimdiff, ecmerge, and opendiff + kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, + ecmerge, diffuse and opendiff + If a diff tool is not specified, 'git-difftool' will use the configuration variable `diff.tool`. If the -- cgit v1.2.3 From afcbc8e7ecb18a3ee542e808f02f5df7d56d5bdc Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Tue, 7 Apr 2009 01:21:20 -0700 Subject: difftool: move 'git-difftool' out of contrib This prepares 'git-difftool' and its documentation for mainstream use. 'git-difftool-helper' became 'git-difftool--helper' since users should not use it directly. 'git-difftool' was added to the list of commands as an ancillaryinterrogator. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/difftool/git-difftool | 76 ------------ contrib/difftool/git-difftool-helper | 221 ----------------------------------- contrib/difftool/git-difftool.txt | 97 --------------- 3 files changed, 394 deletions(-) delete mode 100755 contrib/difftool/git-difftool delete mode 100755 contrib/difftool/git-difftool-helper delete mode 100644 contrib/difftool/git-difftool.txt (limited to 'contrib') diff --git a/contrib/difftool/git-difftool b/contrib/difftool/git-difftool deleted file mode 100755 index 8c160e5bb4..0000000000 --- a/contrib/difftool/git-difftool +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env perl -# Copyright (c) 2009 David Aguilar -# -# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible -# git-difftool-helper script. This script exports -# GIT_EXTERNAL_DIFF and GIT_PAGER for use by git, and -# GIT_DIFFTOOL_NO_PROMPT and GIT_DIFF_TOOL for use by git-difftool-helper. -# Any arguments that are unknown to this script are forwarded to 'git diff'. - -use strict; -use warnings; -use Cwd qw(abs_path); -use File::Basename qw(dirname); - -my $DIR = abs_path(dirname($0)); - - -sub usage -{ - print << 'USAGE'; -usage: git difftool [--tool=] [-y|--no-prompt] ["git diff" options] -USAGE - exit 1; -} - -sub setup_environment -{ - $ENV{PATH} = "$DIR:$ENV{PATH}"; - $ENV{GIT_PAGER} = ''; - $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool-helper'; -} - -sub exe -{ - my $exe = shift; - if ($^O eq 'MSWin32' || $^O eq 'msys') { - return "$exe.exe"; - } - return $exe; -} - -sub generate_command -{ - my @command = (exe('git'), 'diff'); - my $skip_next = 0; - my $idx = -1; - for my $arg (@ARGV) { - $idx++; - if ($skip_next) { - $skip_next = 0; - next; - } - if ($arg eq '-t' || $arg eq '--tool') { - usage() if $#ARGV <= $idx; - $ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1]; - $skip_next = 1; - next; - } - if ($arg =~ /^--tool=/) { - $ENV{GIT_DIFF_TOOL} = substr($arg, 7); - next; - } - if ($arg eq '-y' || $arg eq '--no-prompt') { - $ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true'; - next; - } - if ($arg eq '-h' || $arg eq '--help') { - usage(); - } - push @command, $arg; - } - return @command -} - -setup_environment(); -exec(generate_command()); diff --git a/contrib/difftool/git-difftool-helper b/contrib/difftool/git-difftool-helper deleted file mode 100755 index 4b0daec5a7..0000000000 --- a/contrib/difftool/git-difftool-helper +++ /dev/null @@ -1,221 +0,0 @@ -#!/bin/sh -# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher. -# This script is typically launched by using the 'git difftool' -# convenience command. -# -# Copyright (c) 2009 David Aguilar - -# Set GIT_DIFFTOOL_NO_PROMPT to bypass the per-file prompt. -should_prompt () { - test -z "$GIT_DIFFTOOL_NO_PROMPT" -} - -# This function prepares temporary files and launches the appropriate -# merge tool. -launch_merge_tool () { - # Merged is the filename as it appears in the work tree - # Local is the contents of a/filename - # Remote is the contents of b/filename - # Custom merge tool commands might use $BASE so we provide it - MERGED="$1" - LOCAL="$2" - REMOTE="$3" - BASE="$1" - - # $LOCAL and $REMOTE are temporary files so prompt - # the user with the real $MERGED name before launching $merge_tool. - if should_prompt; then - printf "\nViewing: '$MERGED'\n" - printf "Hit return to launch '%s': " "$merge_tool" - read ans - fi - - # Run the appropriate merge tool command - case "$merge_tool" in - kdiff3) - basename=$(basename "$MERGED") - "$merge_tool_path" --auto \ - --L1 "$basename (A)" \ - --L2 "$basename (B)" \ - "$LOCAL" "$REMOTE" \ - > /dev/null 2>&1 - ;; - - kompare) - "$merge_tool_path" "$LOCAL" "$REMOTE" - ;; - - tkdiff) - "$merge_tool_path" "$LOCAL" "$REMOTE" - ;; - - meld) - "$merge_tool_path" "$LOCAL" "$REMOTE" - ;; - - diffuse) - "$merge_tool_path" "$LOCAL" "$REMOTE" | cat - ;; - - vimdiff) - "$merge_tool_path" -d -c "wincmd l" "$LOCAL" "$REMOTE" - ;; - - gvimdiff) - "$merge_tool_path" -d -c "wincmd l" -f "$LOCAL" "$REMOTE" - ;; - - xxdiff) - "$merge_tool_path" \ - -R 'Accel.Search: "Ctrl+F"' \ - -R 'Accel.SearchForward: "Ctrl-G"' \ - "$LOCAL" "$REMOTE" - ;; - - opendiff) - "$merge_tool_path" "$LOCAL" "$REMOTE" | cat - ;; - - ecmerge) - "$merge_tool_path" "$LOCAL" "$REMOTE" \ - --default --mode=merge2 --to="$MERGED" - ;; - - emerge) - "$merge_tool_path" -f emerge-files-command \ - "$LOCAL" "$REMOTE" "$(basename "$MERGED")" - ;; - - *) - if test -n "$merge_tool_cmd"; then - ( eval $merge_tool_cmd ) - fi - ;; - esac -} - -# Verifies that (difftool|mergetool)..cmd exists -valid_custom_tool() { - merge_tool_cmd="$(git config difftool.$1.cmd)" - test -z "$merge_tool_cmd" && - merge_tool_cmd="$(git config mergetool.$1.cmd)" - test -n "$merge_tool_cmd" -} - -# Verifies that the chosen merge tool is properly setup. -# Built-in merge tools are always valid. -valid_tool() { - case "$1" in - kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge) - ;; # happy - *) - if ! valid_custom_tool "$1" - then - return 1 - fi - ;; - esac -} - -# Sets up the merge_tool_path variable. -# This handles the difftool..path configuration. -# This also falls back to mergetool defaults. -init_merge_tool_path() { - merge_tool_path=$(git config difftool."$1".path) - test -z "$merge_tool_path" && - merge_tool_path=$(git config mergetool."$1".path) - if test -z "$merge_tool_path"; then - case "$1" in - vimdiff) - merge_tool_path=vim - ;; - gvimdiff) - merge_tool_path=gvim - ;; - emerge) - merge_tool_path=emacs - ;; - *) - merge_tool_path="$1" - ;; - esac - fi -} - -# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values -test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL" -test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL" - -# If merge tool was not specified then use the diff.tool -# configuration variable. If that's invalid then reset merge_tool. -# Fallback to merge.tool. -if test -z "$merge_tool"; then - merge_tool=$(git config diff.tool) - test -z "$merge_tool" && - merge_tool=$(git config merge.tool) - if test -n "$merge_tool" && ! valid_tool "$merge_tool"; then - echo >&2 "git config option diff.tool set to unknown tool: $merge_tool" - echo >&2 "Resetting to default..." - unset merge_tool - fi -fi - -# Try to guess an appropriate merge tool if no tool has been set. -if test -z "$merge_tool"; then - # We have a $DISPLAY so try some common UNIX merge tools - if test -n "$DISPLAY"; then - # If gnome then prefer meld, otherwise, prefer kdiff3 or kompare - if test -n "$GNOME_DESKTOP_SESSION_ID" ; then - merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff diffuse" - else - merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff diffuse" - fi - fi - if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then - # $EDITOR is emacs so add emerge as a candidate - merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff" - elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then - # $EDITOR is vim so add vimdiff as a candidate - merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge" - else - merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff" - fi - echo "merge tool candidates: $merge_tool_candidates" - - # Loop over each candidate and stop when a valid merge tool is found. - for i in $merge_tool_candidates - do - init_merge_tool_path $i - if type "$merge_tool_path" > /dev/null 2>&1; then - merge_tool=$i - break - fi - done - - if test -z "$merge_tool" ; then - echo "No known merge resolution program available." - exit 1 - fi - -else - # A merge tool has been set, so verify that it's valid. - if ! valid_tool "$merge_tool"; then - echo >&2 "Unknown merge tool $merge_tool" - exit 1 - fi - - init_merge_tool_path "$merge_tool" - - if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then - echo "The merge tool $merge_tool is not available as '$merge_tool_path'" - exit 1 - fi -fi - - -# Launch the merge tool on each path provided by 'git diff' -while test $# -gt 6 -do - launch_merge_tool "$1" "$2" "$5" - shift 7 -done diff --git a/contrib/difftool/git-difftool.txt b/contrib/difftool/git-difftool.txt deleted file mode 100644 index af68466ebc..0000000000 --- a/contrib/difftool/git-difftool.txt +++ /dev/null @@ -1,97 +0,0 @@ -git-difftool(1) -=============== - -NAME ----- -git-difftool - Show changes using common diff tools - -SYNOPSIS --------- -'git difftool' [--tool=] [-y|--no-prompt] [<'git diff' options>] - -DESCRIPTION ------------ -'git-difftool' is a git command that allows you to compare and edit files -between revisions using common diff tools. 'git difftool' is a frontend -to 'git-diff' and accepts the same options and arguments. - -OPTIONS -------- --y:: ---no-prompt:: - Do not prompt before launching a diff tool. - --t :: ---tool=:: - Use the diff tool specified by . - Valid merge tools are: - kdiff3, kompare, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, - ecmerge, diffuse and opendiff -+ -If a diff tool is not specified, 'git-difftool' -will use the configuration variable `diff.tool`. If the -configuration variable `diff.tool` is not set, 'git-difftool' -will pick a suitable default. -+ -You can explicitly provide a full path to the tool by setting the -configuration variable `difftool..path`. For example, you -can configure the absolute path to kdiff3 by setting -`difftool.kdiff3.path`. Otherwise, 'git-difftool' assumes the -tool is available in PATH. -+ -Instead of running one of the known diff tools, -'git-difftool' can be customized to run an alternative program -by specifying the command line to invoke in a configuration -variable `difftool..cmd`. -+ -When 'git-difftool' is invoked with this tool (either through the -`-t` or `--tool` option or the `diff.tool` configuration variable) -the configured command line will be invoked with the following -variables available: `$LOCAL` is set to the name of the temporary -file containing the contents of the diff pre-image and `$REMOTE` -is set to the name of the temporary file containing the contents -of the diff post-image. `$BASE` is provided for compatibility -with custom merge tool commands and has the same value as `$LOCAL`. - -See linkgit:git-diff[1] for the full list of supported options. - -CONFIG VARIABLES ----------------- -'git-difftool' falls back to 'git-mergetool' config variables when the -difftool equivalents have not been defined. - -diff.tool:: - The default diff tool to use. - -difftool..path:: - Override the path for the given tool. This is useful in case - your tool is not in the PATH. - -difftool..cmd:: - Specify the command to invoke the specified diff tool. -+ -See the `--tool=` option above for more details. - -SEE ALSO --------- -linkgit:git-diff[1]:: - Show changes between commits, commit and working tree, etc - -linkgit:git-mergetool[1]:: - Run merge conflict resolution tools to resolve merge conflicts - -linkgit:git-config[1]:: - Get and set repository or global options - - -AUTHOR ------- -Written by David Aguilar . - -Documentation --------------- -Documentation by David Aguilar and the git-list . - -GIT ---- -Part of the linkgit:git[1] suite -- cgit v1.2.3 From e2dc2de917778a0601564e238c3cd61614f55e5f Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Mon, 6 Apr 2009 01:31:27 -0700 Subject: bash completion: add git-difftool This adds completion for difftool's --tool flag. The known diff tool names were also consolidated into a single variable. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index e099ed48ff..069e19e82a 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -910,6 +910,26 @@ _git_diff () __git_complete_file } +__git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff + tkdiff vimdiff gvimdiff xxdiff +" + +_git_difftool () +{ + local cur="${COMP_WORDS[COMP_CWORD]}" + case "$cur" in + --tool=*) + __gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}" + return + ;; + --*) + __gitcomp "--tool=" + return + ;; + esac + COMPREPLY=() +} + __git_fetch_options=" --quiet --verbose --append --upload-pack --force --keep --depth= --tags --no-tags @@ -1172,11 +1192,7 @@ _git_mergetool () local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in --tool=*) - __gitcomp " - kdiff3 tkdiff meld xxdiff emerge - vimdiff gvimdiff ecmerge diffuse - opendiff - " "" "${cur##--tool=}" + __gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}" return ;; --*) @@ -1901,6 +1917,7 @@ _git () config) _git_config ;; describe) _git_describe ;; diff) _git_diff ;; + difftool) _git_difftool ;; fetch) _git_fetch ;; format-patch) _git_format_patch ;; fsck) _git_fsck ;; -- cgit v1.2.3 From 680ebc01806187b33cab9093c39102468298350f Mon Sep 17 00:00:00 2001 From: Mike Ralphson Date: Fri, 17 Apr 2009 19:13:28 +0100 Subject: Documentation: fix typos / spelling mistakes Signed-off-by: Mike Ralphson Signed-off-by: Junio C Hamano --- contrib/thunderbird-patch-inline/README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/thunderbird-patch-inline/README b/contrib/thunderbird-patch-inline/README index 39f96aa115..000147bbe4 100644 --- a/contrib/thunderbird-patch-inline/README +++ b/contrib/thunderbird-patch-inline/README @@ -1,12 +1,12 @@ appp.sh is a script that is supposed to be used together with ExternalEditor -for Mozilla Thundebird. It will let you include patches inline in e-mails +for Mozilla Thunderbird. It will let you include patches inline in e-mails in an easy way. Usage: - Generate the patch with git format-patch. - Start writing a new e-mail in Thunderbird. - Press the external editor button (or Ctrl-E) to run appp.sh -- Select the previosly generated patch file. +- Select the previously generated patch file. - Finish editing the e-mail. Any text that is entered into the message editor before appp.sh is called -- cgit v1.2.3 From 6123d7196fdd9ac9aed24b4aeed854813fe9a6b1 Mon Sep 17 00:00:00 2001 From: Markus Heidelberg Date: Sat, 25 Apr 2009 13:46:14 +0200 Subject: bash completion: show-branch color support This implements completion of --color and --no-color for "git show-branch" and color.showbranch for "git config". Signed-off-by: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1a90cb87f5..b588387262 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1333,7 +1333,8 @@ _git_config () __gitcomp "$(__git_merge_strategies)" return ;; - color.branch|color.diff|color.interactive|color.status|color.ui) + color.branch|color.diff|color.interactive|\ + color.showbranch|color.status|color.ui) __gitcomp "always never auto" return ;; @@ -1415,6 +1416,7 @@ _git_config () color.interactive.help color.interactive.prompt color.pager + color.showbranch color.status color.status.added color.status.changed @@ -1676,6 +1678,7 @@ _git_show_branch () __gitcomp " --all --remotes --topo-order --current --more= --list --independent --merge-base --no-name + --color --no-color --sha1-name --topics --reflog " return -- cgit v1.2.3 From 4b25d091ba53c758fae0096b8c0662371857b9d9 Mon Sep 17 00:00:00 2001 From: Felipe Contreras Date: Fri, 1 May 2009 12:06:36 +0300 Subject: Fix a bunch of pointer declarations (codestyle) Essentially; s/type* /type */ as per the coding guidelines. Signed-off-by: Felipe Contreras Signed-off-by: Junio C Hamano --- contrib/convert-objects/convert-objects.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/convert-objects/convert-objects.c b/contrib/convert-objects/convert-objects.c index 90e7900e6d..f3b57bf1d2 100644 --- a/contrib/convert-objects/convert-objects.c +++ b/contrib/convert-objects/convert-objects.c @@ -59,7 +59,7 @@ static void convert_ascii_sha1(void *buffer) struct entry *entry; if (get_sha1_hex(buffer, sha1)) - die("expected sha1, got '%s'", (char*) buffer); + die("expected sha1, got '%s'", (char *) buffer); entry = convert_entry(sha1); memcpy(buffer, sha1_to_hex(entry->new_sha1), 40); } @@ -100,7 +100,7 @@ static int write_subdirectory(void *buffer, unsigned long size, const char *base if (!slash) { newlen += sprintf(new + newlen, "%o %s", mode, path); new[newlen++] = '\0'; - hashcpy((unsigned char*)new + newlen, (unsigned char *) buffer + len - 20); + hashcpy((unsigned char *)new + newlen, (unsigned char *) buffer + len - 20); newlen += 20; used += len; @@ -271,7 +271,7 @@ static void convert_commit(void *buffer, unsigned long size, unsigned char *resu unsigned long orig_size = size; if (memcmp(buffer, "tree ", 5)) - die("Bad commit '%s'", (char*) buffer); + die("Bad commit '%s'", (char *) buffer); convert_ascii_sha1((char *) buffer + 5); buffer = (char *) buffer + 46; /* "tree " + "hex sha1" + "\n" */ while (!memcmp(buffer, "parent ", 7)) { -- cgit v1.2.3 From 226b343cde7624e4f273a45d83009799447c914b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 3 May 2009 23:25:31 -0700 Subject: completion: add missing configuration variables to _git_config() Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1a90cb87f5..28682a79be 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1393,6 +1393,7 @@ _git_config () ;; esac __gitcomp " + alias. apply.whitespace branch.autosetupmerge branch.autosetuprebase @@ -1410,6 +1411,9 @@ _git_config () color.diff.old color.diff.plain color.diff.whitespace + color.grep + color.grep.external + color.grep.match color.interactive color.interactive.header color.interactive.help @@ -1427,6 +1431,7 @@ _git_config () core.autocrlf core.bare core.compression + core.createObject core.deltaBaseCacheLimit core.editor core.excludesfile @@ -1457,11 +1462,20 @@ _git_config () diff.renameLimit diff.renameLimit. diff.renames + diff.suppressBlankEmpty + diff.tool + diff.wordRegex + difftool.prompt fetch.unpackLimit + format.attach + format.cc format.headers format.numbered format.pretty + format.signoff + format.subjectprefix format.suffix + format.thread gc.aggressiveWindow gc.auto gc.autopacklimit @@ -1472,6 +1486,7 @@ _git_config () gc.rerereresolved gc.rerereunresolved gitcvs.allbinary + gitcvs.commitmsgannotation gitcvs.dbTableNamePrefix gitcvs.dbdriver gitcvs.dbname @@ -1506,13 +1521,23 @@ _git_config () http.sslVerify i18n.commitEncoding i18n.logOutputEncoding + imap.folder + imap.host + imap.pass + imap.port + imap.preformattedHTML + imap.sslverify + imap.tunnel + imap.user instaweb.browser instaweb.httpd instaweb.local instaweb.modulepath instaweb.port + interactive.singlekey log.date log.showroot + mailmap.file man.viewer merge.conflictstyle merge.log @@ -1521,6 +1546,7 @@ _git_config () merge.tool merge.verbosity mergetool.keepBackup + mergetool.prompt pack.compression pack.deltaCacheLimit pack.deltaCacheSize @@ -1532,6 +1558,8 @@ _git_config () pack.windowMemory pull.octopus pull.twohead + push.default + rebase.stat receive.denyCurrentBranch receive.denyDeletes receive.denyNonFastForwards @@ -1540,6 +1568,26 @@ _git_config () repack.usedeltabaseoffset rerere.autoupdate rerere.enabled + sendemail.aliasesfile + sendemail.aliasesfiletype + sendemail.bcc + sendemail.cc + sendemail.cccmd + sendemail.chainreplyto + sendemail.confirm + sendemail.envelopesender + sendemail.multiedit + sendemail.signedoffbycc + sendemail.smtpencryption + sendemail.smtppass + sendemail.smtpserver + sendemail.smtpserverport + sendemail.smtpuser + sendemail.suppresscc + sendemail.suppressfrom + sendemail.thread + sendemail.to + sendemail.validate showbranch.default status.relativePaths status.showUntrackedFiles -- cgit v1.2.3 From 0aa62fd0414b0e8a6271d1d0dd80e5f640633473 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 3 May 2009 23:25:32 -0700 Subject: completion: add {gui,diff,merge}tool, man, and pager config variables Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 45 ++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 28682a79be..ec02b06cf2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1376,6 +1376,39 @@ _git_config () __gitcomp "$(__git_heads)" "$pfx" "$cur" "." return ;; + guitool.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + __gitcomp " + argprompt cmd confirm needsfile noconsole norescan + prompt revprompt revunmerged title + " "$pfx" "$cur" + return + ;; + difftool.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + __gitcomp "cmd path" "$pfx" "$cur" + return + ;; + man.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + __gitcomp "cmd path" "$pfx" "$cur" + return + ;; + mergetool.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + __gitcomp "cmd path trustExitCode" "$pfx" "$cur" + return + ;; + pager.*) + local pfx="${cur%.*}." + cur="${cur#*.}" + __gitcomp "$(__git_all_commands)" "$pfx" "$cur" + return + ;; remote.*.*) local pfx="${cur%.*}." cur="${cur##*.}" @@ -1391,6 +1424,12 @@ _git_config () __gitcomp "$(__git_remotes)" "$pfx" "$cur" "." return ;; + url.*.*) + local pfx="${cur%.*}." + cur="${cur##*.}" + __gitcomp "insteadof" "$pfx" "$cur" + return + ;; esac __gitcomp " alias. @@ -1465,6 +1504,7 @@ _git_config () diff.suppressBlankEmpty diff.tool diff.wordRegex + difftool. difftool.prompt fetch.unpackLimit format.attach @@ -1495,6 +1535,7 @@ _git_config () gitcvs.enabled gitcvs.logfile gitcvs.usecrlfattr + guitool. gui.blamehistoryctx gui.commitmsgwidth gui.copyblamethreshold @@ -1538,6 +1579,7 @@ _git_config () log.date log.showroot mailmap.file + man. man.viewer merge.conflictstyle merge.log @@ -1545,6 +1587,7 @@ _git_config () merge.stat merge.tool merge.verbosity + mergetool. mergetool.keepBackup mergetool.prompt pack.compression @@ -1556,6 +1599,7 @@ _git_config () pack.threads pack.window pack.windowMemory + pager. pull.octopus pull.twohead push.default @@ -1593,6 +1637,7 @@ _git_config () status.showUntrackedFiles tar.umask transfer.unpackLimit + url. user.email user.name user.signingkey -- cgit v1.2.3 From 9b82d63b5a109112643843a8e6d1a201fdf2ec63 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 3 May 2009 23:25:33 -0700 Subject: completion: complete values for help.format Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index ec02b06cf2..023b0c9974 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1348,6 +1348,10 @@ _git_config () " return ;; + help.format) + __gitcomp "man info web html" + return + ;; *.*) COMPREPLY=() return -- cgit v1.2.3 From 672c68cbb90921a133ddf71d002342a448b6dd38 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 3 May 2009 23:25:34 -0700 Subject: completion: complete values for log.date Add raw to the date formats too. Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 023b0c9974..d67ffd9384 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1116,6 +1116,7 @@ __git_log_shortlog_options=" " __git_log_pretty_formats="oneline short medium full fuller email raw format:" +__git_log_date_formats="relative iso8601 rfc2822 short local default raw" _git_log () { @@ -1139,9 +1140,7 @@ _git_log () return ;; --date=*) - __gitcomp " - relative iso8601 rfc2822 short local default - " "" "${cur##--date=}" + __gitcomp "$__git_log_date_formats" "" "${cur##--date=}" return ;; --*) @@ -1352,6 +1351,10 @@ _git_config () __gitcomp "man info web html" return ;; + log.date) + __gitcomp "$__git_log_date_formats" + return + ;; *.*) COMPREPLY=() return -- cgit v1.2.3 From ae616de6d53ef14ba11d2aa32f366086c1435dfa Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sun, 3 May 2009 23:25:35 -0700 Subject: completion: complete values for send-email Add completion for --confirm, --suppress-cc, and --smtp-encryption command line arguments. Add completion for aliasfiletype and confirm configuration variables. Since --smtp-ssl is deprecated, replace it with --smtp-encryption and the two options ssl and tls. Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 39 +++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index d67ffd9384..1683e6d7b8 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1282,18 +1282,39 @@ _git_rebase () __gitcomp "$(__git_refs)" } +__git_send_email_confirm_options="always never auto cc compose" +__git_send_email_suppresscc_options="author self cc ccbody sob cccmd body all" + _git_send_email () { local cur="${COMP_WORDS[COMP_CWORD]}" case "$cur" in + --confirm=*) + __gitcomp " + $__git_send_email_confirm_options + " "" "${cur##--confirm=}" + return + ;; + --suppress-cc=*) + __gitcomp " + $__git_send_email_suppresscc_options + " "" "${cur##--suppress-cc=}" + + return + ;; + --smtp-encryption=*) + __gitcomp "ssl tls" "" "${cur##--smtp-encryption=}" + return + ;; --*) __gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to - --compose --dry-run --envelope-sender --from --identity + --compose --confirm= --dry-run --envelope-sender + --from --identity --in-reply-to --no-chain-reply-to --no-signed-off-by-cc --no-suppress-from --no-thread --quiet --signed-off-by-cc --smtp-pass --smtp-server - --smtp-server-port --smtp-ssl --smtp-user --subject - --suppress-cc --suppress-from --thread --to + --smtp-server-port --smtp-encryption= --smtp-user + --subject --suppress-cc= --suppress-from --thread --to --validate --no-validate" return ;; @@ -1355,6 +1376,18 @@ _git_config () __gitcomp "$__git_log_date_formats" return ;; + sendemail.aliasesfiletype) + __gitcomp "mutt mailrc pine elm gnus" + return + ;; + sendemail.confirm) + __gitcomp "$__git_send_email_confirm_options" + return + ;; + sendemail.suppresscc) + __gitcomp "$__git_send_email_suppresscc_options" + return + ;; *.*) COMPREPLY=() return -- cgit v1.2.3 From 00652369ff285f84e440f5d41d708283e30825d7 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 8 May 2009 18:23:32 -0700 Subject: bash completion: complete variable names for "git config" with options This makes it easier for users to get and unset their configuration variables without having to open documentation or dig through their configuration file. __git_config_get_set_variables() retrieves the set configuration variables from the appropriate configuration file. For example, if the user has previously specified --global only the global variables are returned. The same applies for --system, and --file. If no location has been specified, all set variables are returned. Signed-off-by: Stephen Boyd Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1683e6d7b8..ad26b7c5ae 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1322,6 +1322,35 @@ _git_send_email () COMPREPLY=() } +__git_config_get_set_variables () +{ + local prevword word config_file= c=$COMP_CWORD + while [ $c -gt 1 ]; do + word="${COMP_WORDS[c]}" + case "$word" in + --global|--system|--file=*) + config_file="$word" + break + ;; + -f|--file) + config_file="$word $prevword" + break + ;; + esac + prevword=$word + c=$((--c)) + done + + for i in $(git --git-dir="$(__gitdir)" config $config_file --list \ + 2>/dev/null); do + case "$i" in + *.*) + echo "${i/=*/}" + ;; + esac + done +} + _git_config () { local cur="${COMP_WORDS[COMP_CWORD]}" @@ -1388,6 +1417,10 @@ _git_config () __gitcomp "$__git_send_email_suppresscc_options" return ;; + --get|--get-all|--unset|--unset-all) + __gitcomp "$(__git_config_get_set_variables)" + return + ;; *.*) COMPREPLY=() return -- cgit v1.2.3 From 8763dbb1b24c260243f69130c734c13563a16db6 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 16 May 2009 11:46:22 -0700 Subject: completion: fix PS1 display during a merge on detached HEAD If your merge stops in a conflict while on a detached HEAD, recent completion code fails to show anything. This was because various cases added to support the operation-in-progress markers (e.g. REBASE, MERGING) forgot that they need to set the variable "b" to something for the result they computed to be displayed at all. Probably not many people make trial merges on a detached HEAD (which is tremendously useful feature of git, by the way), and that may be why this was not noticed for a long time. Acked-By: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1683e6d7b8..c2f8ea3444 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -99,10 +99,10 @@ __git_ps1 () elif [ -d "$g/rebase-merge" ]; then r="|REBASE-m" b="$(cat "$g/rebase-merge/head-name")" - elif [ -f "$g/MERGE_HEAD" ]; then - r="|MERGING" - b="$(git symbolic-ref HEAD 2>/dev/null)" else + if [ -f "$g/MERGE_HEAD" ]; then + r="|MERGING" + fi if [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi -- cgit v1.2.3 From ff790b6a4bb7fa3bbccd5ea23cefd89da900aa2e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 May 2009 01:53:19 -0700 Subject: completion: simplify "current branch" in __git_ps1() As I very often work on a detached HEAD, I found it pretty confusing when __git_ps1() said 'some-name'. Did I create a branch with that name by mistake, or do I happen to be on a commit with that exact tag? This patch fixes the issue by enclosing non branch names in a pair of parentheses when used to substitute %s token in __git_ps1() argument. It also fixes a small bug where the branch part is left empty when .git/HEAD is unreadable for whatever reason. The output now says "(unknown)". Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c2f8ea3444..be591468db 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -106,13 +106,14 @@ __git_ps1 () if [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi - if ! b="$(git symbolic-ref HEAD 2>/dev/null)"; then - if ! b="$(git describe --exact-match HEAD 2>/dev/null)"; then - if [ -r "$g/HEAD" ]; then - b="$(cut -c1-7 "$g/HEAD")..." - fi - fi - fi + + b="$(git symbolic-ref HEAD 2>/dev/null)" || { + b="$(git describe --exact-match HEAD 2>/dev/null)" || + b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." || + b="unknown" + + b="($b)" + } fi local w -- cgit v1.2.3 From dd42c2f015102626562da05bb290f47862ea06fb Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 10 May 2009 01:56:21 -0700 Subject: completion: enhance "current branch" display Introduce GIT_PS1_DESCRIBE option you can set to "contains", "branch", or "describe" to tweak the way how a detached HEAD is described. The default behaviour is to describe only exact match with some tag (otherwise use the first 7 hexdigits) as before. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index be591468db..dd6cd250e3 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -108,10 +108,21 @@ __git_ps1 () fi b="$(git symbolic-ref HEAD 2>/dev/null)" || { - b="$(git describe --exact-match HEAD 2>/dev/null)" || + + b="$( + case "${GIT_PS1_DESCRIBE_STYLE-}" in + (contains) + git describe --contains HEAD ;; + (branch) + git describe --contains --all HEAD ;; + (describe) + git describe HEAD ;; + (* | default) + git describe --exact-match HEAD ;; + esac 2>/dev/null)" || + b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." || b="unknown" - b="($b)" } fi -- cgit v1.2.3 From 076c32370d8a6ac2fb57b2a55c674942e106f8ab Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 16 May 2009 20:42:43 -0700 Subject: completion: add missing options to show-branch and show Add --oneline and --abbrev-commit to show and --sparse to show-branch. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index dd6cd250e3..a0c5794828 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1804,7 +1804,7 @@ _git_show () return ;; --*) - __gitcomp "--pretty= --format= + __gitcomp "--pretty= --format= --abbrev-commit --oneline $__git_diff_common_options " return @@ -1821,7 +1821,7 @@ _git_show_branch () __gitcomp " --all --remotes --topo-order --current --more= --list --independent --merge-base --no-name - --sha1-name --topics --reflog + --sha1-name --sparse --topics --reflog " return ;; -- cgit v1.2.3 From 8dfb17e1fd7dec1d3a1978eb46743964c481cd08 Mon Sep 17 00:00:00 2001 From: Giuseppe Bilotta Date: Mon, 18 May 2009 18:24:30 +0200 Subject: completion: use git rev-parse to detect bare repos Its check is more robust than a config check for core.bare Trivially-Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index a0c5794828..f44152c433 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -132,7 +132,7 @@ __git_ps1 () local c if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then - if [ "true" = "$(git config --bool core.bare 2>/dev/null)" ]; then + if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then c="BARE:" else b="GIT_DIR!" -- cgit v1.2.3 From 5ffd3113d4109ae5d3595425af3ff4a781617631 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 23 May 2009 14:26:44 +0200 Subject: post-receive-email: hooks.showrev: show how to include both web link and patch Add a comment showing how to include a web link (i.e. gitweb/cgit) and a patch in the email that is sent for each pushed commit. The quoting was tricky enough that it's worth documenting. To add two blank lines (i.e. put \n\n in the printf), you would need to say \\\\n\\\\n, and in the end, the pair of "echo" statements seemed better. This is used in glibc.git repository: http://sources.redhat.com/git/gitweb.cgi?p=glibc.git;a=summary push-triggered messages have been sent to this list since May 21: http://sourceware.org/ml/glibc-cvs/2009-q2/ Signed-off-by: Junio C Hamano --- contrib/hooks/post-receive-email | 4 ++++ 1 file changed, 4 insertions(+) mode change 100644 => 100755 contrib/hooks/post-receive-email (limited to 'contrib') diff --git a/contrib/hooks/post-receive-email b/contrib/hooks/post-receive-email old mode 100644 new mode 100755 index 60cbab65d3..2a66063e44 --- a/contrib/hooks/post-receive-email +++ b/contrib/hooks/post-receive-email @@ -44,6 +44,10 @@ # --pretty %s", displaying the commit id, author, date and log # message. To list full patches separated by a blank line, you # could set this to "git show -C %s; echo". +# To list a gitweb/cgit URL *and* a full patch for each change set, use this: +# "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo" +# Be careful if "..." contains things that will be expanded by shell "eval" +# or printf. # # Notes # ----- -- cgit v1.2.3 From b6f0621a462e4ff11999c8e2d8279c0ffd3be201 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sun, 24 May 2009 00:24:41 +0000 Subject: mergetool--lib: add support for araxis merge Araxis merge is now a built-in diff/merge tool. This adds araxis to git-completion and updates the documentation to mention araxis. Signed-off-by: David Aguilar Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1683e6d7b8..ead530d3a3 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -911,7 +911,7 @@ _git_diff () } __git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff - tkdiff vimdiff gvimdiff xxdiff + tkdiff vimdiff gvimdiff xxdiff araxis " _git_difftool () -- cgit v1.2.3 From d7107ca65f79c2d95bc327bee44c09073b4de631 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 25 May 2009 22:56:03 -0700 Subject: completion: fix PS1 display during an AM on detached HEAD This is a companion patch to previous 8763dbb (completion: fix PS1 display during a merge on detached HEAD, 2009-05-16). While rebasing or running am on a detached HEAD, the code failed to set $b (branch description) that enables the whole status display business. Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 0c8bb536c8..c84d765ff9 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -84,26 +84,24 @@ __git_ps1 () if [ -n "$g" ]; then local r local b - if [ -d "$g/rebase-apply" ]; then - if [ -f "$g/rebase-apply/rebasing" ]; then - r="|REBASE" - elif [ -f "$g/rebase-apply/applying" ]; then - r="|AM" - else - r="|AM/REBASE" - fi - b="$(git symbolic-ref HEAD 2>/dev/null)" - elif [ -f "$g/rebase-merge/interactive" ]; then + if [ -f "$g/rebase-merge/interactive" ]; then r="|REBASE-i" b="$(cat "$g/rebase-merge/head-name")" elif [ -d "$g/rebase-merge" ]; then r="|REBASE-m" b="$(cat "$g/rebase-merge/head-name")" else - if [ -f "$g/MERGE_HEAD" ]; then + if [ -d "$g/rebase-apply" ]; then + if [ -f "$g/rebase-apply/rebasing" ]; then + r="|REBASE" + elif [ -f "$g/rebase-apply/applying" ]; then + r="|AM" + else + r="|AM/REBASE" + fi + elif [ -f "$g/MERGE_HEAD" ]; then r="|MERGING" - fi - if [ -f "$g/BISECT_LOG" ]; then + elif [ -f "$g/BISECT_LOG" ]; then r="|BISECTING" fi -- cgit v1.2.3 From ee6b71141f2803bd4d4f6e1a17ef826567dc9644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Fri, 29 May 2009 14:00:36 +0200 Subject: bash: remove always true if statement from __git_ps1() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recent commits 8763dbb1 (completion: fix PS1 display during a merge on detached HEAD, 2009-05-16), ff790b6a (completion: simplify "current branch" in __git_ps1(), 2009-05-10), and d7107ca6 (completion: fix PS1 display during an AM on detached HEAD, 2009-05-26) ensure that the branch name in __git_ps1() is always set to something sensible. Therefore, the condition for checking the non-empty branch name is always fulfilled, and can be removed. Signed-off-by: SZEDER Gábor Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index c84d765ff9..98b9cbedc2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -150,12 +150,10 @@ __git_ps1 () fi fi - if [ -n "$b" ]; then - if [ -n "${1-}" ]; then - printf "$1" "$c${b##refs/heads/}$w$i$r" - else - printf " (%s)" "$c${b##refs/heads/}$w$i$r" - fi + if [ -n "${1-}" ]; then + printf "$1" "$c${b##refs/heads/}$w$i$r" + else + printf " (%s)" "$c${b##refs/heads/}$w$i$r" fi fi } -- cgit v1.2.3 From 2414b45ce0c56f7744e00e7fef3682a77375701e Mon Sep 17 00:00:00 2001 From: Daniel Trstenjak Date: Tue, 2 Jun 2009 20:03:22 +0200 Subject: Show presence of stashed changes in bash prompt. Add a '$' in the __git_ps1 output to show stashed changes are present, when GIT_PS1_SHOWSTASHSTATE is set to a nonempty value. The code for checking if the stash has entries is taken from 'git-stash.sh'. Signed-off-by: Daniel Trstenjak Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 98b9cbedc2..80190a6b16 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -40,6 +40,10 @@ # with the bash.showDirtyState variable, which defaults to true # once GIT_PS1_SHOWDIRTYSTATE is enabled. # +# You can also see if currently something is stashed, by setting +# GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed, +# then a '$' will be shown next to the branch name. +# # To submit patches: # # *) Read Documentation/SubmittingPatches @@ -127,6 +131,7 @@ __git_ps1 () local w local i + local s local c if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then @@ -148,12 +153,15 @@ __git_ps1 () fi fi fi + if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then + git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$" + fi fi if [ -n "${1-}" ]; then - printf "$1" "$c${b##refs/heads/}$w$i$r" + printf "$1" "$c${b##refs/heads/}$w$i$s$r" else - printf " (%s)" "$c${b##refs/heads/}$w$i$r" + printf " (%s)" "$c${b##refs/heads/}$w$i$s$r" fi fi } -- cgit v1.2.3 From 8513c54b2a4a821336d10fae6e02db70f0876abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Tue, 9 Jun 2009 00:57:39 +0200 Subject: bash: add support for 'git stash pop --index' option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 80190a6b16..cc94ef44b2 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1881,10 +1881,10 @@ _git_stash () save,--*) __gitcomp "--keep-index" ;; - apply,--*) + apply,--*|pop,--*) __gitcomp "--index" ;; - show,--*|drop,--*|pop,--*|branch,--*) + show,--*|drop,--*|branch,--*) COMPREPLY=() ;; show,*|apply,*|drop,*|pop,*|branch,*) -- cgit v1.2.3 From 6fb37f86bce7cf19b4dbff2aa8a93df5ac5c4cbe Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 17 Jun 2009 14:49:39 +0200 Subject: import-tars: support symlinks Without this patch, symbolic links are turned into empty files. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- contrib/fast-import/import-tars.perl | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl index 6309d146e7..78e40d2a13 100755 --- a/contrib/fast-import/import-tars.perl +++ b/contrib/fast-import/import-tars.perl @@ -82,10 +82,16 @@ foreach my $tar_file (@ARGV) $mtime = oct $mtime; next if $typeflag == 5; # directory - print FI "blob\n", "mark :$next_mark\n", "data $size\n"; - while ($size > 0 && read(I, $_, 512) == 512) { - print FI substr($_, 0, $size); - $size -= 512; + print FI "blob\n", "mark :$next_mark\n"; + if ($typeflag == 2) { # symbolic link + print FI "data ", length($linkname), "\n", $linkname; + $mode = 0120000; + } else { + print FI "data $size\n"; + while ($size > 0 && read(I, $_, 512) == 512) { + print FI substr($_, 0, $size); + $size -= 512; + } } print FI "\n"; @@ -118,7 +124,8 @@ EOF { my ($mark, $mode) = @{$files{$path}}; $path =~ s,^([^/]+)/,, if $have_top_dir; - printf FI "M %o :%i %s\n", $mode & 0111 ? 0755 : 0644, $mark, $path; + $mode = $mode & 0111 ? 0755 : 0644 unless $mode == 0120000; + printf FI "M %o :%i %s\n", $mode, $mark, $path; } print FI "\n"; -- cgit v1.2.3 From cb8a9bd518002dd4fb693df6230b4976bafc15e0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 18 Jun 2009 14:31:32 +0200 Subject: Test cccmd in t9001-send-email.sh and fix some bugs For another patch series I'm working on I needed some tests for the cc-cmd feature of git-send-email. This patch adds 3 tests for the feature and for the possibility to specify --suppress-cc multiple times, and fixes two bugs. The first bug is that the --suppress-cc option for `cccmd' was misspelled as `ccmd' in the code. The second bug, which is actually found only with my other series, is that the argument to the cccmd is never quoted, so the cccmd would fail with patch file names containing a space. A third bug I fix (in the docs) is that the bodycc argument was actually spelled ccbody in the documentation and bash completion. Signed-off-by: Paolo Bonzini Cc: Markus Heidelberg Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 80ab4e45eb..b60cb68a8b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1299,7 +1299,7 @@ _git_rebase () } __git_send_email_confirm_options="always never auto cc compose" -__git_send_email_suppresscc_options="author self cc ccbody sob cccmd body all" +__git_send_email_suppresscc_options="author self cc bodycc sob cccmd body all" _git_send_email () { -- cgit v1.2.3 From 6fac1b83bdb9aee73363f93874ffaffd1bc3ad5c Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 29 Jun 2009 21:24:24 -0700 Subject: completion: add missing config variables Update to include branch.*.rebase, remote.*.pushurl, and add.ignore-errors Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index b60cb68a8b..ddb71e26fe 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1457,7 +1457,7 @@ _git_config () branch.*.*) local pfx="${cur%.*}." cur="${cur##*.}" - __gitcomp "remote merge mergeoptions" "$pfx" "$cur" + __gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur" return ;; branch.*) @@ -1504,7 +1504,7 @@ _git_config () cur="${cur##*.}" __gitcomp " url proxy fetch push mirror skipDefaultUpdate - receivepack uploadpack tagopt + receivepack uploadpack tagopt pushurl " "$pfx" "$cur" return ;; @@ -1522,6 +1522,7 @@ _git_config () ;; esac __gitcomp " + add.ignore-errors alias. apply.whitespace branch.autosetupmerge -- cgit v1.2.3 From 5d0e634343bf83735b59c611b96c1d0f8ff2b28a Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Wed, 3 Jun 2009 16:20:58 -0400 Subject: completion: Add --full-diff to log options Signed-off-by: Todd Zullinger Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index ddb71e26fe..1f44ec2092 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1165,7 +1165,7 @@ _git_log () $__git_log_shortlog_options $__git_log_gitk_options --root --topo-order --date-order --reverse - --follow + --follow --full-diff --abbrev-commit --abbrev= --relative-date --date= --pretty= --format= --oneline -- cgit v1.2.3 From f581de1b7b9d17c83b188bf8ffe536fb8a9dd2a4 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 29 Jun 2009 22:08:38 -0700 Subject: completion: __git_config_get_set_variables() handle values with spaces Commit 0065236 (bash completion: complete variable names for "git config" with options 2009-05-08) implemented its config variable search wrong. When a config contains a value with a space and a period (.) in it, completion erroneously thinks that line in the configuration is multiple config variables. For example $ cat .git/config format.cc = Junio C Hamano $ git config --unset format.cc Instead of using a for loop splitting across spaces, pipe each line to a while read loop and beef up the case statement to match only 'config.variable=value'. Signed-off-by: Stephen Boyd Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 1f44ec2092..9c488646d0 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1357,11 +1357,12 @@ __git_config_get_set_variables () c=$((--c)) done - for i in $(git --git-dir="$(__gitdir)" config $config_file --list \ - 2>/dev/null); do - case "$i" in - *.*) - echo "${i/=*/}" + git --git-dir="$(__gitdir)" config $config_file --list 2>/dev/null | + while read line + do + case "$line" in + *.*=*) + echo "${line/=*/}" ;; esac done -- cgit v1.2.3 From 4fe1a61973c82c459ac0a25cb5342d00d347dfd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SZEDER=20G=C3=A1bor?= Date: Mon, 13 Jul 2009 17:11:45 +0200 Subject: bash: add '--merges' to common 'git log' options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... so it's available for git log, shortlog and gitk. Signed-off-by: SZEDER Gábor Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9c488646d0..887731e830 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1114,7 +1114,7 @@ _git_ls_tree () __git_log_common_options=" --not --all --branches --tags --remotes - --first-parent --no-merges + --first-parent --merges --no-merges --max-count= --max-age= --since= --after= --min-age= --until= --before= -- cgit v1.2.3 From 397f7c6371d309aa399bf32b773397f3c068d0c9 Mon Sep 17 00:00:00 2001 From: Daniel Trstenjak Date: Wed, 22 Jul 2009 10:31:34 +0200 Subject: Show the presence of untracked files in the bash prompt. Added the envvar GIT_PS1_SHOWUNTRACKEDFILES to 'git-completion.bash'. When set to a nonempty value, then the char '%' will be shown next to the branch name in the bash prompt. Signed-off-by: Daniel Trstenjak Acked-by: Shawn O. Pearce Signed-off-by: Junio C Hamano --- contrib/completion/git-completion.bash | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'contrib') diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 887731e830..745b5fb78b 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -44,6 +44,10 @@ # GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed, # then a '$' will be shown next to the branch name. # +# If you would like to see if there're untracked files, then you can +# set GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're +# untracked files, then a '%' will be shown next to the branch name. +# # To submit patches: # # *) Read Documentation/SubmittingPatches @@ -132,6 +136,7 @@ __git_ps1 () local w local i local s + local u local c if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then @@ -156,12 +161,18 @@ __git_ps1 () if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$" fi + + if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then + if [ -n "$(git ls-files --others --exclude-standard)" ]; then + u="%" + fi + fi fi if [ -n "${1-}" ]; then - printf "$1" "$c${b##refs/heads/}$w$i$s$r" + printf "$1" "$c${b##refs/heads/}$w$i$s$u$r" else - printf " (%s)" "$c${b##refs/heads/}$w$i$s$r" + printf " (%s)" "$c${b##refs/heads/}$w$i$s$u$r" fi fi } -- cgit v1.2.3