On Thu, Aug 22, 2019 at 04:44:37PM -0400, Christopher Sean Morrison wrote:

> We’re migrating a very large old repository to Git (the oldest in
> continuous development!) and using the notes feature to preserve Svn
> data like revision IDs and branch information.  We’ve run into what
> seems like an incomplete feature or bug in that notes get
> orphaned/disassociated if an old commit is changed (e.g., edit a
> committer's e-mail or the commit message).
> 
> The changed commit and all children commits get sha updates, but the
> notes remain associated with the original sha.  This can be
> particularly catastrophic, for example, if an early commit is changed
> as all notes become orphaned.
> 
> I presume this is a bug but perhaps we may be doing something wrong?
> Is there a better way to preserve old Svn information that will work
> with filter-branch?

Whether the old notes apply to the rewritten commit depends on what is
in the notes. Commands like rebase and "commit --amend" that rewrite
history have some options to carry notes across rewrites. See
"notes.rewrite*" in "git help config".

I don't think that filter-branch ever learned about rewriting notes.
Here's an old thread asking the same thing:

  https://public-inbox.org/git/hbf.20110317i...@bombur.uio.no/

And it even mentions an even more ancient patch:

  
https://public-inbox.org/git/0ad4b8c1a5377d697513cd8e49f64419cd8deef4.1266164150.git.tr...@student.ethz.ch/

The consensus seems to be that filter-branch should actually have a
notes filter, but nobody ever implemented it.

In the meantime, I think you could accomplish the same thing with a
--commit-filter. This seems to work for me:

  # fake repo with some notes
  git init repo
  cd repo
  for i in 1 2 3; do
        echo $i >orig-$i
        git add orig-$i
        git commit -m $i
        git notes add -m "note $i"
  done

  # introduce a silly tree change that will modify the commit ids,
  # and map the notes, too
  git filter-branch \
          --tree-filter 'rename s/orig-/new-/ *' \
          --commit-filter '
                commit=$(git commit-tree "$@")
                git notes copy $GIT_COMMIT $commit
                echo $commit
          '

  # this should have the new-* files, but still have notes
  git log --raw

I think you could also use "--state-branch", and then pass its mapping
into a single "notes copy" invocation, which would be much more
efficient. E.g.:

  git filter-branch \
    --tree-filter 'rename s/orig-/new-/ *' \
    --state-branch refs/mapped-state
  git cat-file blob refs/mapped-state:filter.map |
    tr ':' ' ' |
    git notes copy --stdin

Hope that helps.

-Peff

Reply via email to