@@ -39,28 +39,36 @@ In this documentation, a "git-series commit" refers to a commit corresponding
to a version of an entire patch series, as distinguished from a commit
corresponding to one patch within a patch series.
-The first parent of each git-series commit always points to the previous
-version of the patch series, if any. The remaining parents of each git-series
-commit correspond to commits referenced as gitlinks (tree entries with mode
-160000) within the commit's tree. This ensures that git can reach all of those
-commits. (Note that git's traversal algorithm does not follow gitlink commits
-within tree objects, so without these additional parent links, git would
-consider these gitlink commits unreachable and discard them.)
-
-The second and subsequent parents of each git-series commit do not appear in
-any particular order; do not assume that the `series` object or any other
-gitlink appears at any particular position within the parents list. These
-parents exist only to make commits reachable and transferable by git. Always
-look up commits via named tree entries within the git-series commit's tree
-object.
+A git-series commit can have two types of parent commits: those connecting the
+history of the patch series, and those referencing gitlink commits that also
+appear in the git-series commit's tree. A git-series commit can have any
+number of either type of parent, but all of the parents connecting the history
+of the patch series will always appear before any of the parents referencing
+gitlink commits.
+
+The parents connecting the history of the patch series, if any, point to
+previous git-series commits representing previous versions of the patch series;
+a git-series commit with more than one such parent represents a git-series
+merge commit. The remaining parents of each git-series commit correspond to
+commits referenced as gitlinks (tree entries with mode 160000) within the
+commit's tree; this ensures that git can reach all of those commits. (Note
+that git's traversal algorithm does not follow gitlink commits within tree
+objects, so without these additional parent links, git would consider these
+gitlink commits unreachable and discard them.)
+
+The parents of each git-series commit that reference gitlinks in that
+git-series commit's tree do not appear in any particular order; do not assume
+that the `series` object or any other gitlink appears at any particular
+position within the parents list. These parents exist only to make commits
+reachable and transferable by git. Always look up commits via named tree
+entries within the git-series commit's tree object.
In the root git-series commit, all the parent commits correspond to gitlinks
-within the tree. This will not occur for any non-root commit of a git-series.
-Algorithms trying to walk from a git-series commit to its root should detect
-the root git-series commit by checking if the first parent appears in the
-git-series commit's tree. (This does not require a recursive tree walk; the
-first parent of the git-series root will always appear in the top-level tree
-object.)
+within the git-series commit's tree. This will not occur for any non-root
+commit of a git-series. Algorithms trying to walk git-series commits should
+filter out parents that appear in the git-series commit's tree. (This does not
+require a recursive tree walk; the gitlinks within the git-series commit's tree
+will appear in the top-level tree object.)
git-series tree entries
-----------------------
@@ -1202,41 +1202,66 @@ fn log(out: &mut Output, repo: &Repository, m: &ArgMatches) -> Result<()> {
let color_commit = try!(out.get_color(&config, "diff", "commit", "yellow"));
let mut revwalk = try!(repo.revwalk());
- revwalk.simplify_first_parent();
try!(revwalk.push_ref(SHEAD_REF));
+ // Walk once before sorting, to find all the commits to hide. Revwalk doesn't support hiding on
+ // the fly when sorted.
+ let mut hidden_ids = std::collections::HashSet::new();
+ while let Some(oid) = revwalk.next() {
+ let oid = try!(oid);
+ let commit = try!(repo.find_commit(oid));
+ let tree = try!(commit.tree());
+ for parent_id in commit.parent_ids() {
+ if tree.iter().find(|entry| entry.id() == parent_id).is_some() {
+ try!(revwalk.hide(parent_id));
+ hidden_ids.insert(parent_id);
+ }
+ }
+ }
+
+ // set_sorting resets the revwalk
+ revwalk.set_sorting(git2::SORT_TOPOLOGICAL);
+ try!(revwalk.push_ref(SHEAD_REF));
+ for id in hidden_ids {
+ try!(revwalk.hide(id));
+ }
+
let show_diff = m.is_present("patch");
+ let mut first = true;
for oid in revwalk {
+ if first {
+ first = false;
+ } else {
+ try!(writeln!(out, ""));
+ }
let oid = try!(oid);
let commit = try!(repo.find_commit(oid));
- let tree = try!(commit.tree());
let author = commit.author();
- let first_parent_id = try!(commit.parent_id(0).map_err(|e| format!("Malformed series commit {}: {}", oid, e)));
- let first_series_commit = tree.iter().find(|entry| entry.id() == first_parent_id).is_some();
-
try!(writeln!(out, "{}", color_commit.paint(format!("commit {}", oid))));
try!(writeln!(out, "Author: {} <{}>", author.name().unwrap(), author.email().unwrap()));
try!(writeln!(out, "Date: {}\n", date_822(author.when())));
for line in commit.message().unwrap().lines() {
try!(writeln!(out, " {}", line));
}
+
if show_diff {
- try!(writeln!(out, ""));
- let parent_tree = if first_series_commit {
- None
- } else {
- Some(try!(try!(repo.find_commit(first_parent_id)).tree()))
- };
- let diff = try!(repo.diff_tree_to_tree(parent_tree.as_ref(), Some(&tree), None));
- try!(write_diff(out, &diff));
- }
+ let tree = try!(commit.tree());
+ let parent_ids: Vec<_> = commit.parent_ids().take_while(|parent_id| tree.iter().find(|entry| &entry.id() == parent_id).is_none()).collect();
- if first_series_commit {
- break;
- } else {
try!(writeln!(out, ""));
+ if parent_ids.len() > 1 {
+ try!(writeln!(out, "(Diffs of series merge commits not yet supported)"));
+ } else {
+ let parent_tree = if parent_ids.len() == 0 {
+ None
+ } else {
+ Some(try!(try!(repo.find_commit(parent_ids[0])).tree()))
+ };
+ let diff = try!(repo.diff_tree_to_tree(parent_tree.as_ref(), Some(&tree), None));
+ try!(write_diff(out, &diff));
+ }
}
}