@@ -1718,6 +1718,12 @@ static int is_pick_or_similar(enum todo_command command)
1718
1718
}
1719
1719
}
1720
1720
1721
+ enum todo_item_flags {
1722
+ TODO_EDIT_MERGE_MSG = (1 << 0 ),
1723
+ TODO_REPLACE_FIXUP_MSG = (1 << 1 ),
1724
+ TODO_EDIT_FIXUP_MSG = (1 << 2 ),
1725
+ };
1726
+
1721
1727
static size_t subject_length (const char * body )
1722
1728
{
1723
1729
const char * p = body ;
@@ -1734,32 +1740,176 @@ static size_t subject_length(const char *body)
1734
1740
1735
1741
static const char first_commit_msg_str [] = N_ ("This is the 1st commit message:" );
1736
1742
static const char nth_commit_msg_fmt [] = N_ ("This is the commit message #%d:" );
1743
+ static const char skip_first_commit_msg_str [] = N_ ("The 1st commit message will be skipped:" );
1737
1744
static const char skip_nth_commit_msg_fmt [] = N_ ("The commit message #%d will be skipped:" );
1738
1745
static const char combined_commit_msg_fmt [] = N_ ("This is a combination of %d commits." );
1739
1746
1740
- static void append_squash_message (struct strbuf * buf , const char * body ,
1741
- struct replay_opts * opts )
1747
+ static int check_fixup_flag (enum todo_command command ,
1748
+ enum todo_item_flags flag )
1749
+ {
1750
+ return command == TODO_FIXUP && ((flag & TODO_REPLACE_FIXUP_MSG ) ||
1751
+ (flag & TODO_EDIT_FIXUP_MSG ));
1752
+ }
1753
+
1754
+ /*
1755
+ * Wrapper around strbuf_add_commented_lines() which avoids double
1756
+ * commenting commit subjects.
1757
+ */
1758
+ static void add_commented_lines (struct strbuf * buf , const void * str , size_t len )
1759
+ {
1760
+ const char * s = str ;
1761
+ while (len > 0 && s [0 ] == comment_line_char ) {
1762
+ size_t count ;
1763
+ const char * n = memchr (s , '\n' , len );
1764
+ if (!n )
1765
+ count = len ;
1766
+ else
1767
+ count = n - s + 1 ;
1768
+ strbuf_add (buf , s , count );
1769
+ s += count ;
1770
+ len -= count ;
1771
+ }
1772
+ strbuf_add_commented_lines (buf , s , len );
1773
+ }
1774
+
1775
+ /* Does the current fixup chain contain a squash command? */
1776
+ static int seen_squash (struct replay_opts * opts )
1777
+ {
1778
+ return starts_with (opts -> current_fixups .buf , "squash" ) ||
1779
+ strstr (opts -> current_fixups .buf , "\nsquash" );
1780
+ }
1781
+
1782
+ static void update_comment_bufs (struct strbuf * buf1 , struct strbuf * buf2 , int n )
1742
1783
{
1743
- size_t commented_len = 0 ;
1784
+ strbuf_setlen (buf1 , 2 );
1785
+ strbuf_addf (buf1 , _ (nth_commit_msg_fmt ), n );
1786
+ strbuf_addch (buf1 , '\n' );
1787
+ strbuf_setlen (buf2 , 2 );
1788
+ strbuf_addf (buf2 , _ (skip_nth_commit_msg_fmt ), n );
1789
+ strbuf_addch (buf2 , '\n' );
1790
+ }
1744
1791
1745
- unlink (rebase_path_fixup_msg ());
1746
- if (starts_with (body , "squash!" ) || starts_with (body , "fixup!" ))
1792
+ /*
1793
+ * Comment out any un-commented commit messages, updating the message comments
1794
+ * to say they will be skipped but do not comment out the empty lines that
1795
+ * surround commit messages and their comments.
1796
+ */
1797
+ static void update_squash_message_for_fixup (struct strbuf * msg )
1798
+ {
1799
+ void (* copy_lines )(struct strbuf * , const void * , size_t ) = strbuf_add ;
1800
+ struct strbuf buf1 = STRBUF_INIT , buf2 = STRBUF_INIT ;
1801
+ const char * s , * start ;
1802
+ char * orig_msg ;
1803
+ size_t orig_msg_len ;
1804
+ int i = 1 ;
1805
+
1806
+ strbuf_addf (& buf1 , "# %s\n" , _ (first_commit_msg_str ));
1807
+ strbuf_addf (& buf2 , "# %s\n" , _ (skip_first_commit_msg_str ));
1808
+ s = start = orig_msg = strbuf_detach (msg , & orig_msg_len );
1809
+ while (s ) {
1810
+ const char * next ;
1811
+ size_t off ;
1812
+ if (skip_prefix (s , buf1 .buf , & next )) {
1813
+ /*
1814
+ * Copy the last message, preserving the blank line
1815
+ * preceding the current line
1816
+ */
1817
+ off = (s > start + 1 && s [-2 ] == '\n' ) ? 1 : 0 ;
1818
+ copy_lines (msg , start , s - start - off );
1819
+ if (off )
1820
+ strbuf_addch (msg , '\n' );
1821
+ /*
1822
+ * The next message needs to be commented out but the
1823
+ * message header is already commented out so just copy
1824
+ * it and the blank line that follows it.
1825
+ */
1826
+ strbuf_addbuf (msg , & buf2 );
1827
+ if (* next == '\n' )
1828
+ strbuf_addch (msg , * next ++ );
1829
+ start = s = next ;
1830
+ copy_lines = add_commented_lines ;
1831
+ update_comment_bufs (& buf1 , & buf2 , ++ i );
1832
+ } else if (skip_prefix (s , buf2 .buf , & next )) {
1833
+ off = (s > start + 1 && s [-2 ] == '\n' ) ? 1 : 0 ;
1834
+ copy_lines (msg , start , s - start - off );
1835
+ start = s - off ;
1836
+ s = next ;
1837
+ copy_lines = strbuf_add ;
1838
+ update_comment_bufs (& buf1 , & buf2 , ++ i );
1839
+ } else {
1840
+ s = strchr (s , '\n' );
1841
+ if (s )
1842
+ s ++ ;
1843
+ }
1844
+ }
1845
+ copy_lines (msg , start , orig_msg_len - (start - orig_msg ));
1846
+ free (orig_msg );
1847
+ strbuf_release (& buf1 );
1848
+ strbuf_release (& buf2 );
1849
+ }
1850
+
1851
+ static int append_squash_message (struct strbuf * buf , const char * body ,
1852
+ enum todo_command command , struct replay_opts * opts ,
1853
+ enum todo_item_flags flag )
1854
+ {
1855
+ const char * fixup_msg ;
1856
+ size_t commented_len = 0 , fixup_off ;
1857
+ /*
1858
+ * amend is non-interactive and not normally used with fixup!
1859
+ * or squash! commits, so only comment out those subjects when
1860
+ * squashing commit messages.
1861
+ */
1862
+ if (starts_with (body , "amend!" ) ||
1863
+ ((command == TODO_SQUASH || seen_squash (opts )) &&
1864
+ (starts_with (body , "squash!" ) || starts_with (body , "fixup!" ))))
1747
1865
commented_len = subject_length (body );
1866
+
1748
1867
strbuf_addf (buf , "\n%c " , comment_line_char );
1749
1868
strbuf_addf (buf , _ (nth_commit_msg_fmt ),
1750
1869
++ opts -> current_fixup_count + 1 );
1751
1870
strbuf_addstr (buf , "\n\n" );
1752
1871
strbuf_add_commented_lines (buf , body , commented_len );
1872
+ /* buf->buf may be reallocated so store an offset into the buffer */
1873
+ fixup_off = buf -> len ;
1753
1874
strbuf_addstr (buf , body + commented_len );
1875
+
1876
+ /* fixup -C after squash behaves like squash */
1877
+ if (check_fixup_flag (command , flag ) && !seen_squash (opts )) {
1878
+ /*
1879
+ * We're replacing the commit message so we need to
1880
+ * append the Signed-off-by: trailer if the user
1881
+ * requested '--signoff'.
1882
+ */
1883
+ if (opts -> signoff )
1884
+ append_signoff (buf , 0 , 0 );
1885
+
1886
+ if ((command == TODO_FIXUP ) &&
1887
+ (flag & TODO_REPLACE_FIXUP_MSG ) &&
1888
+ (file_exists (rebase_path_fixup_msg ()) ||
1889
+ !file_exists (rebase_path_squash_msg ()))) {
1890
+ fixup_msg = skip_blank_lines (buf -> buf + fixup_off );
1891
+ if (write_message (fixup_msg , strlen (fixup_msg ),
1892
+ rebase_path_fixup_msg (), 0 ) < 0 )
1893
+ return error (_ ("cannot write '%s'" ),
1894
+ rebase_path_fixup_msg ());
1895
+ } else {
1896
+ unlink (rebase_path_fixup_msg ());
1897
+ }
1898
+ } else {
1899
+ unlink (rebase_path_fixup_msg ());
1900
+ }
1901
+
1902
+ return 0 ;
1754
1903
}
1755
1904
1756
1905
static int update_squash_messages (struct repository * r ,
1757
1906
enum todo_command command ,
1758
1907
struct commit * commit ,
1759
- struct replay_opts * opts )
1908
+ struct replay_opts * opts ,
1909
+ enum todo_item_flags flag )
1760
1910
{
1761
1911
struct strbuf buf = STRBUF_INIT ;
1762
- int res ;
1912
+ int res = 0 ;
1763
1913
const char * message , * body ;
1764
1914
const char * encoding = get_commit_output_encoding ();
1765
1915
@@ -1779,6 +1929,8 @@ static int update_squash_messages(struct repository *r,
1779
1929
opts -> current_fixup_count + 2 );
1780
1930
strbuf_splice (& buf , 0 , eol - buf .buf , header .buf , header .len );
1781
1931
strbuf_release (& header );
1932
+ if (check_fixup_flag (command , flag ) && !seen_squash (opts ))
1933
+ update_squash_message_for_fixup (& buf );
1782
1934
} else {
1783
1935
struct object_id head ;
1784
1936
struct commit * head_commit ;
@@ -1792,18 +1944,22 @@ static int update_squash_messages(struct repository *r,
1792
1944
return error (_ ("could not read HEAD's commit message" ));
1793
1945
1794
1946
find_commit_subject (head_message , & body );
1795
- if (command == TODO_FIXUP && write_message (body , strlen (body ),
1947
+ if (command == TODO_FIXUP && ! flag && write_message (body , strlen (body ),
1796
1948
rebase_path_fixup_msg (), 0 ) < 0 ) {
1797
1949
unuse_commit_buffer (head_commit , head_message );
1798
1950
return error (_ ("cannot write '%s'" ), rebase_path_fixup_msg ());
1799
1951
}
1800
-
1801
1952
strbuf_addf (& buf , "%c " , comment_line_char );
1802
1953
strbuf_addf (& buf , _ (combined_commit_msg_fmt ), 2 );
1803
1954
strbuf_addf (& buf , "\n%c " , comment_line_char );
1804
- strbuf_addstr (& buf , _ (first_commit_msg_str ));
1955
+ strbuf_addstr (& buf , check_fixup_flag (command , flag ) ?
1956
+ _ (skip_first_commit_msg_str ) :
1957
+ _ (first_commit_msg_str ));
1805
1958
strbuf_addstr (& buf , "\n\n" );
1806
- strbuf_addstr (& buf , body );
1959
+ if (check_fixup_flag (command , flag ))
1960
+ strbuf_add_commented_lines (& buf , body , strlen (body ));
1961
+ else
1962
+ strbuf_addstr (& buf , body );
1807
1963
1808
1964
unuse_commit_buffer (head_commit , head_message );
1809
1965
}
@@ -1813,8 +1969,8 @@ static int update_squash_messages(struct repository *r,
1813
1969
oid_to_hex (& commit -> object .oid ));
1814
1970
find_commit_subject (message , & body );
1815
1971
1816
- if (command == TODO_SQUASH ) {
1817
- append_squash_message (& buf , body , opts );
1972
+ if (command == TODO_SQUASH || check_fixup_flag ( command , flag ) ) {
1973
+ res = append_squash_message (& buf , body , command , opts , flag );
1818
1974
} else if (command == TODO_FIXUP ) {
1819
1975
strbuf_addf (& buf , "\n%c " , comment_line_char );
1820
1976
strbuf_addf (& buf , _ (skip_nth_commit_msg_fmt ),
@@ -1825,7 +1981,9 @@ static int update_squash_messages(struct repository *r,
1825
1981
return error (_ ("unknown command: %d" ), command );
1826
1982
unuse_commit_buffer (commit , message );
1827
1983
1828
- res = write_message (buf .buf , buf .len , rebase_path_squash_msg (), 0 );
1984
+ if (!res )
1985
+ res = write_message (buf .buf , buf .len , rebase_path_squash_msg (),
1986
+ 0 );
1829
1987
strbuf_release (& buf );
1830
1988
1831
1989
if (!res ) {
@@ -2026,7 +2184,8 @@ static int do_pick_commit(struct repository *r,
2026
2184
if (command == TODO_REWORD )
2027
2185
reword = 1 ;
2028
2186
else if (is_fixup (command )) {
2029
- if (update_squash_messages (r , command , commit , opts ))
2187
+ if (update_squash_messages (r , command , commit ,
2188
+ opts , item -> flags ))
2030
2189
return -1 ;
2031
2190
flags |= AMEND_MSG ;
2032
2191
if (!final_fixup )
@@ -2191,10 +2350,6 @@ static int read_and_refresh_cache(struct repository *r,
2191
2350
return 0 ;
2192
2351
}
2193
2352
2194
- enum todo_item_flags {
2195
- TODO_EDIT_MERGE_MSG = 1
2196
- };
2197
-
2198
2353
void todo_list_release (struct todo_list * todo_list )
2199
2354
{
2200
2355
strbuf_release (& todo_list -> buf );
@@ -2281,6 +2436,18 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
2281
2436
return 0 ;
2282
2437
}
2283
2438
2439
+ if (item -> command == TODO_FIXUP ) {
2440
+ if (skip_prefix (bol , "-C" , & bol ) &&
2441
+ (* bol == ' ' || * bol == '\t' )) {
2442
+ bol += strspn (bol , " \t" );
2443
+ item -> flags |= TODO_REPLACE_FIXUP_MSG ;
2444
+ } else if (skip_prefix (bol , "-c" , & bol ) &&
2445
+ (* bol == ' ' || * bol == '\t' )) {
2446
+ bol += strspn (bol , " \t" );
2447
+ item -> flags |= TODO_EDIT_FIXUP_MSG ;
2448
+ }
2449
+ }
2450
+
2284
2451
if (item -> command == TODO_MERGE ) {
2285
2452
if (skip_prefix (bol , "-C" , & bol ))
2286
2453
bol += strspn (bol , " \t" );
@@ -5287,6 +5454,14 @@ static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_lis
5287
5454
short_commit_name (item -> commit ) :
5288
5455
oid_to_hex (& item -> commit -> object .oid );
5289
5456
5457
+ if (item -> command == TODO_FIXUP ) {
5458
+ if (item -> flags & TODO_EDIT_FIXUP_MSG )
5459
+ strbuf_addstr (buf , " -c" );
5460
+ else if (item -> flags & TODO_REPLACE_FIXUP_MSG ) {
5461
+ strbuf_addstr (buf , " -C" );
5462
+ }
5463
+ }
5464
+
5290
5465
if (item -> command == TODO_MERGE ) {
5291
5466
if (item -> flags & TODO_EDIT_MERGE_MSG )
5292
5467
strbuf_addstr (buf , " -c" );
0 commit comments