monotone.patch 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. Fix "mtn diff" output by handling the special cases of missing trailing
  2. newline at end of file. This makes the output compatible with GNU
  3. diffutils' diff(1) and especially allows it to be correctly applied with
  4. GNU patch(1) again, too.
  5. The source of the problem is that diff_patch:cc:make_diff() called the
  6. generic function simplestring_xform.cc:split_into_lines() for splitting
  7. the file data into individual lines, this way removed all(!) newlines at
  8. all and hence lost the information about the special case of a missing
  9. trailing newline.
  10. The least invasive fix is to already preserve the information in
  11. simplestring_xform.cc:split_into_lines() by representing the missing
  12. newline with the appended text "[\r]\n\\No newline at end of file" on
  13. the last line (notice that this is the only case were a line can still
  14. contain a newline after splitting).
  15. Additionally, diff_patch.cc:walk_hunk_consumer() for unknown reasons
  16. produces added before deleted lines (instead of the expection order of
  17. deleted before added lines) and hence also had to be fixed in order to
  18. produce output 100% compatible to GNU diff(1).
  19. Finally, two existing tests related to diff_patch.cc:walk_hunk_consumer()
  20. were adjusted and a new test suite was added for checking all four
  21. special cases related to the missing trailing newline. The complete
  22. Monotone test suite still succeeed with 0 failures after this change.
  23. Can be removed with Monotone >= 0.40!
  24. Index: diff_patch.cc
  25. --- diff_patch.cc e359a4c03c6ce548d6414dce11160fbcb6fcb831
  26. +++ diff_patch.cc daeb9be7d8c10d33781b54debd541027618dbfce
  27. @@ -1010,17 +1010,17 @@ void walk_hunk_consumer(vector<long, QA(
  28. while (idx(lines2,b) != *i)
  29. cons.insert_at(b++);
  30. }
  31. - if (b < lines2.size())
  32. + if (a < lines1.size())
  33. {
  34. cons.advance_to(a);
  35. - while(b < lines2.size())
  36. - cons.insert_at(b++);
  37. + while(a < lines1.size())
  38. + cons.delete_at(a++);
  39. }
  40. - if (a < lines1.size())
  41. + if (b < lines2.size())
  42. {
  43. cons.advance_to(a);
  44. - while(a < lines1.size())
  45. - cons.delete_at(a++);
  46. + while(b < lines2.size())
  47. + cons.insert_at(b++);
  48. }
  49. cons.flush_hunk(a);
  50. }
  51. @@ -1349,8 +1349,8 @@ make_diff(string const & filename1,
  52. }
  53. vector<string> lines1, lines2;
  54. - split_into_lines(data1(), lines1);
  55. - split_into_lines(data2(), lines2);
  56. + split_into_lines(data1(), lines1, true);
  57. + split_into_lines(data2(), lines2, true);
  58. vector<long, QA(long)> left_interned;
  59. vector<long, QA(long)> right_interned;
  60. Index: simplestring_xform.cc
  61. --- simplestring_xform.cc 5f2e4fcd288a2ec1fd59feb97ebc777b56597608
  62. +++ simplestring_xform.cc dcaa75eb0ed67ddc119ef62f06c21bd7d13bf5cb
  63. @@ -50,9 +50,24 @@ void split_into_lines(string const & in,
  64. }
  65. void split_into_lines(string const & in,
  66. + vector<string> & out,
  67. + bool diff_compat)
  68. +{
  69. + return split_into_lines(in, constants::default_encoding, out, diff_compat);
  70. +}
  71. +
  72. +void split_into_lines(string const & in,
  73. string const & encoding,
  74. vector<string> & out)
  75. {
  76. + return split_into_lines(in, encoding, out, false);
  77. +}
  78. +
  79. +void split_into_lines(string const & in,
  80. + string const & encoding,
  81. + vector<string> & out,
  82. + bool diff_compat)
  83. +{
  84. string lc_encoding = lowercase(encoding);
  85. out.clear();
  86. @@ -92,8 +107,16 @@ void split_into_lines(string const & in,
  87. break;
  88. end = in.find_first_of("\r\n", begin);
  89. }
  90. - if (begin < in.size())
  91. - out.push_back(in.substr(begin, in.size() - begin));
  92. + if (begin < in.size()) {
  93. + // special case: last line without trailing newline
  94. + string s = in.substr(begin, in.size() - begin);
  95. + if (diff_compat) {
  96. + // special handling: produce diff(1) compatible output
  97. + s += (in.find_first_of("\r") != string::npos ? "\r\n" : "\n");
  98. + s += "\\ No newline at end of file";
  99. + }
  100. + out.push_back(s);
  101. + }
  102. }
  103. else
  104. {
  105. Index: simplestring_xform.hh
  106. --- simplestring_xform.hh 0280d49b889bc0b7b2900d5d123cc8ec95a115eb
  107. +++ simplestring_xform.hh 564a305c99ca931c93956ac3ef903683dcb15db7
  108. @@ -13,6 +13,15 @@ void split_into_lines(std::string const
  109. std::string const & encoding,
  110. std::vector<std::string> & out);
  111. +void split_into_lines(std::string const & in,
  112. + std::vector<std::string> & out,
  113. + bool diff_compat);
  114. +
  115. +void split_into_lines(std::string const & in,
  116. + std::string const & encoding,
  117. + std::vector<std::string> & out,
  118. + bool diff_compat);
  119. +
  120. void join_lines(std::vector<std::string> const & in,
  121. std::string & out,
  122. std::string const & linesep);
  123. -----------------------------------------------------------------------------
  124. Fix merging in a special case.
  125. Can be removed with Monotone >= 0.40!
  126. --- roster_merge.cc d565f9299ca85d77bd1d7f2a29196d7161f0ea08
  127. +++ roster_merge.cc d113e5d648bd98bdc15ba7a53958496ea924bd2b
  128. @@ -525,15 +525,35 @@ roster_merge_result::report_duplicate_na
  129. left_roster.get_name(left_nid, left_name);
  130. right_roster.get_name(right_nid, right_name);
  131. - I(left_name == right_name);
  132. -
  133. shared_ptr<roster_t const> left_lca_roster, right_lca_roster;
  134. revision_id left_lca_rid, right_lca_rid;
  135. adaptor.get_ancestral_roster(left_nid, left_lca_rid, left_lca_roster);
  136. adaptor.get_ancestral_roster(right_nid, right_lca_rid, right_lca_roster);
  137. - P(F("conflict: duplicate name '%s'") % left_name);
  138. + // In most cases, the left_name equals the right_name. However, maybe
  139. + // a parent directory got renamed on one side. In that case, the names
  140. + // don't match, but it's still the same directory (by node id), to
  141. + // which we want to add the same file (by name).
  142. + if (left_name == right_name)
  143. + {
  144. + file_path dir;
  145. + path_component basename;
  146. + left_name.dirname_basename(dir, basename);
  147. + P(F("conflict: duplicate name '%s' for the directory '%s'") % basename % dir);
  148. + }
  149. + else
  150. + {
  151. + file_path left_dir, right_dir;
  152. + path_component left_basename, right_basename;
  153. + left_name.dirname_basename(left_dir, left_basename);
  154. + right_name.dirname_basename(right_dir, right_basename);
  155. + I(left_basename == right_basename);
  156. + P(F("conflict: duplicate name '%s' for the directory\n"
  157. + " named '%s' on the left and\n"
  158. + " named '%s' on the right.")
  159. + % left_basename % left_dir % right_dir);
  160. + }
  161. node_type left_type = get_type(left_roster, left_nid);
  162. node_type right_type = get_type(right_roster, right_nid);