2
0

patch.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. # patch.py -- For dealing wih packed-style patches.
  2. # Copryight (C) 2009 Jelmer Vernooij <jelmer@samba.org>
  3. #
  4. # This program is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU General Public License
  6. # as published by the Free Software Foundation; version 2
  7. # of the License or (at your option) a later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  17. # MA 02110-1301, USA.
  18. """Classes for dealing with git am-style patches.
  19. These patches are basically unified diffs with some extra metadata tacked
  20. on.
  21. """
  22. import difflib
  23. import subprocess
  24. import time
  25. def write_commit_patch(f, commit, contents, progress, version=None):
  26. """Write a individual file patch.
  27. :param commit: Commit object
  28. :param progress: Tuple with current patch number and total.
  29. :return: tuple with filename and contents
  30. """
  31. (num, total) = progress
  32. f.write("From %s %s\n" % (commit.id, time.ctime(commit.commit_time)))
  33. f.write("From: %s\n" % commit.author)
  34. f.write("Date: %s\n" % time.strftime("%a, %d %b %Y %H:%M:%S %Z"))
  35. f.write("Subject: [PATCH %d/%d] %s\n" % (num, total, commit.message))
  36. f.write("\n")
  37. f.write("---\n")
  38. try:
  39. p = subprocess.Popen(["diffstat"], stdout=subprocess.PIPE,
  40. stdin=subprocess.PIPE)
  41. except OSError, e:
  42. pass # diffstat not available?
  43. else:
  44. (diffstat, _) = p.communicate(contents)
  45. f.write(diffstat)
  46. f.write("\n")
  47. f.write(contents)
  48. f.write("-- \n")
  49. if version is None:
  50. from dulwich import __version__ as dulwich_version
  51. f.write("Dulwich %d.%d.%d\n" % dulwich_version)
  52. else:
  53. f.write("%s\n" % version)
  54. def get_summary(commit):
  55. """Determine the summary line for use in a filename.
  56. :param commit: Commit
  57. :return: Summary string
  58. """
  59. return commit.message.splitlines()[0].replace(" ", "-")
  60. def write_blob_diff(f, (old_path, old_mode, old_blob),
  61. (new_path, new_mode, new_blob)):
  62. """Write diff file header.
  63. :param f: File-like object to write to
  64. :param (old_path, old_mode, old_blob): Previous file (None if nonexisting)
  65. :param (new_path, new_mode, new_blob): New file (None if nonexisting)
  66. """
  67. def blob_id(blob):
  68. if blob is None:
  69. return "0" * 7
  70. else:
  71. return blob.id[:7]
  72. def lines(blob):
  73. if blob is not None:
  74. return blob.data.splitlines(True)
  75. else:
  76. return []
  77. if old_path is None:
  78. old_path = "/dev/null"
  79. else:
  80. old_path = "a/%s" % old_path
  81. if new_path is None:
  82. new_path = "/dev/null"
  83. else:
  84. new_path = "b/%s" % new_path
  85. f.write("diff --git %s %s\n" % (old_path, new_path))
  86. if old_mode != new_mode:
  87. if new_mode is not None:
  88. if old_mode is not None:
  89. f.write("old file mode %o\n" % old_mode)
  90. f.write("new file mode %o\n" % new_mode)
  91. else:
  92. f.write("deleted file mode %o\n" % old_mode)
  93. f.write("index %s..%s %o\n" % (
  94. blob_id(old_blob), blob_id(new_blob), new_mode))
  95. old_contents = lines(old_blob)
  96. new_contents = lines(new_blob)
  97. f.writelines(difflib.unified_diff(old_contents, new_contents,
  98. old_path, new_path))