From: Olaf Dabrunz Date: Sat, 09 Oct 2010 01:43:18 GMT Subject: [TopGit PATCH] t/depend-add-using-export Message-ID: <1286588598-4005-1-git-send-email-odabrunz@gmx.net> When dependencies can be removed as well as added, tg depend add needs to make sure that the new dependency can bring in changes from a branch that has previously been removed as a dependency from the current TopGit branch. This implementation uses an exported branch set up by tg export, and merges the new dependency into the commit that corresponds to the current base. Using the exported branch in the merge has the advantage that removed dependencies do not appear as parents, and the merge base selected by git merge does not include changes from a removed dependency. As a result, these changes can be merged in again if the new dependency brings in these changes. The tree of the merge commit is then used to create the next commit on the TopGit base branch. Uwe Kleine-König had the idea to use tg export here. Signed-off-by: Olaf Dabrunz --- This is an experimental implementation. It did not get much testing, mainly because testing ran into a fatal "tg export" bug with my test repository. "tg export" could be convinced to continue past the error with "bash -x", and that is how this patch was tested. This is also my first feature patch for TopGit, so I expect people to have comments. Known issues: - .top* files in the base should probably be excluded with pretty_tree - handle tg export producing zero commits for a dependency tg-depend.sh | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 80 insertions(+), 0 deletions(-) diff --git a/tg-depend.sh b/tg-depend.sh index 474ccda..4d1572d 100644 --- a/tg-depend.sh +++ b/tg-depend.sh @@ -60,6 +60,86 @@ depend_add() grep -F -x -e "$name" "$root_dir/.topdeps" >/dev/null && die "tg: $current_name already depends on $name" + tmp_export="_tg_tmp/tg_depend" + trap 'git branch -D "$tmp_export" 2>/dev/null || true' EXIT + + # Create a separate history that does not contain parent pointers to + # removed dependencies. This makes sure that these dependencies can + # come in again if any of them were merged into the new dependency. + # (I.e., not having any parents leading to removed deps makes sure that + # "git merge new_dep" selects a merge base that does not already + # include the removed dependencies.) + + # tg export sets up a branch with the following properties: + # - For each of the exported TopGit branch and it's recursive TopGit + # dependencies, zero, one or two commits are created. If there is + # more than one dependency, one commit is created that contains the + # tree from the tip of the base branch (i.e., the merge of all + # current dependencies). If the TopGit patch branch is non-empty, + # another commit contains the collapsed patch. + # - Only current dependencies from .topdeps are considered; removed + # dependencies are not part of the history of the branch. + # - The .top* files are removed. + # + # tg export bails if the TopGit branch is not up-to-date. + $tg export "$tmp_export" || exit + + # Now checkout the commit representing the base of the current branch + # in the export, and use that to merge in the new dep, resulting in the + # new base tree. + # FIXME: also handle the zero commits case -- does TopGit actually make + # that case possible? + if branch_empty "$current_name"; then + # There is no patch commit. Use tmp_export directly. + git checkout -q "$tmp_export" + else + # Set $tmp_export to it's parent commit, which has the merged + # deps without the current patch on top, and check that out. + # FIXME: keep the tree of the HEAD commit with the collapsed + # patch / new dep merge resolution and use it in the commit on + # the patch branch, instead of running tg update? -- this would + # re-use conflict resolutions (and other changes) -- but would + # git rerere not kick in anyway? + git update-ref "refs/heads/$tmp_export" "$tmp_export^" + git checkout -q "$tmp_export" + fi + + # merge the new dependency into the temp export + if ! git merge "$name"; then + # open up a shell to work on the conflict + info "You are in a subshell. If you abort the merge," + info "use \`exit 1\` to abort adding the dependency." + if ! sh -i >"$root_dir/.topdeps" git add -f "$root_dir/.topdeps" git commit -m"New TopGit dependency: $name" -- tg: (9404aa1..) t/depend-add-using-export (depends on: master)