How to reinstall git repository to parent folder while saving history?

I have a git repo in /foo/bar with a big commit history and several branches.

Now I want /foo/baz be in the same repo as /foo/bar , which (I think) means I need to create a new repo in /foo . However, I want to keep a history of the changes I made to /foo/bar .

At first I thought about git format-patch, and then about apply, but the commit messages were not saved.

Edit: my question is not a deception of this question , because mine is specifically about preserving history, and the answer here includes additional information on how to save history, and additional information on how to perform more complex work of raising children for more high level, not less than once.

+50
git
Jul 09 '10 at 12:20
source share
7 answers

You want a git filter-branch that can move the entire repository into a subtree, keeping the history, making it look like it always has been. Back up your vault before using this!

Here is the magic. In /foo/bar run:

 git filter-branch --commit-filter ' TREE="$1"; shift; SUBTREE=`echo -e 040000 tree $TREE"\tbar" | git mktree` git commit-tree $SUBTREE "$@"' -- --all 

This will make the /foo/bar repository another subdirectory of "bar" with all its contents throughout its history. Then you can move the entire repo to the foo level and add the baz code to it.

Update:

Ok, here's what happens. The end is a link to the "tree" (think of it as a SHA representing the contents of a subdirectory of the entire file system) plus some "parent" SHAs and some authors / metadata links / messages, etc. The git commit-tree command is a low-level bit that brings it all together. The --commit-filter parameter is processed as a shell function and is run instead of git commit-tree during the filtering process and should act like it.

What I am doing is taking the first parameter, the original tree to commit, and creating a new β€œtree object” that talks about this in the subfolder using git mktree , another low-level git command. To do this, I need to enclose something similar to the git tree in it, that is, a set (type SP of type SP SHA TAB filename) of strings; thus the echo command. The output signal mktree then replaced with the first parameter when the I-chain is before the real commit-tree ; "$@" is a way to pass all other parameters without changes, dividing the first by shift . See git help mktree and git help commit-tree .

So, if you need several levels, you need to nest several additional levels of tree objects (this is not tested, but the general idea):

 git filter-branch --commit-filter ' TREE="$1" shift SUBTREE1=`echo -e 040000 tree $TREE"\tbar" | git mktree` SUBTREE2=`echo -e 040000 tree $SUBTREE1"\tb" | git mktree` SUBTREE3=`echo -e 040000 tree $SUBTREE2"\ta" | git mktree` git commit-tree $SUBTREE3 "$@"' -- --all 

This should move the real content down a/b/bar (note the reverse order).

Update . Integrated improvements. Matvey Alpert below. Without -- --all this only works in the current verified branch, but since the question asks a question about the general repo, it makes sense to do it like a fork.

+25
Jul 09 '10 at 12:50
source share

Instead of creating a new repository, move what is in your current repository to the right place: create a new directory bar in your current directory and move the current content (so that your code is in /foo/bar/bar ). Then create a baz directory next to your new bar directory ( /foo/bar/baz ). mv /foo /foo2; mv /foo2/bar /foo; rmdir /foo2 mv /foo /foo2; mv /foo2/bar /foo; rmdir /foo2 and you are done :).

Git Renaming Tracking means that your story will work anyway, and Git content hashing means that even if you move things, you still refer to the same objects in the repository.

+16
Jul 09 '10 at 12:37 on
source share

I had a decision that no one has said so far:

I especially needed to include files from the parent directory in my repository (effectively moving the repo to one directory).

I have achieved this:

  • move all files (except .git) to a new subdirectory with the same name. And tell git about it (with git mv )
  • move all the files from the parent directory to the empty (except .git /) current directory and tell git about it (using git add )
  • pass all this to a repo that didn't move ( git commit ).
  • move the current directory one level in the directory hierarchy. (from jiggery-pokery command line)

I hope this helps the next guy to come - I have probably just a brainless day, but I found the answers above to be overly complex and scary (for which I need it). I know that this is similar to the answer of Andrew Islett above, but my situation seemed a little different, and I wanted to get a more general view.

+6
Aug 23 '13 at 17:49 on
source share

This adds to Walter Mundt's accepted answer. I would rather comment on his answer, but I have no reputation.

So Walter Mundt's method works fine, but it only works for one branch at a time. And after the first branch there may be warnings requiring -f to force action. To do this for all branches at once, simply add "- --all" to the end:

 git filter-branch --commit-filter ' tree="$1"; shift; subtree=`echo -e 040000 tree $tree"\tsrc" | git mktree` git commit-tree $subtree "$@"' -- --all 

And to do this for specific branches, add their names to the end instead, although I cannot imagine why you changed the directory structure of only some branches.

Read more about this on the manual page for the git filter branch. However, please pay attention to the warning about possible difficulties when pressing such a command. Just make sure you know what you are doing.

I would like more information about any potential problems with this method.

+5
Jun 06 2018-12-12T00:
source share

Most common solution

In most normal situations, git scans all files relative to its location (which means the .git directory), as opposed to using absolute file paths.

That way, if you don't mind having a commit in your story that shows that you have moved everything, there is a very simple solution which is to move the git directory. The only slightly complicated task is to make sure that git understands that the files are the same and that they only move relative to it:

 # Create sub-directory with the same name in /foo/bar mkdir bar # Move everything down, notifying git : git mv file1 file2 file3 bar/ # Then move everything up one level : mv .git ../.git mv bar/* . mv .gitignore ../ # Here, take care to move untracked files # Then delete unused directory rmdir bar # and commit cd ../ git commit 

The only thing to be careful is to correctly update .gitignore when moving to a new directory to avoid placing unwanted files or forget some.

Bonus Solution

In some settings, git manages to find out on its own that the files were moved when it sees new files that are exactly the same as deleted ones. In this case, the solution is even simpler:

 mv .git ../.git mv .gitignore ../.gitignore cd ../ git commit 

Again, be careful with your .gitignore

+3
Apr 11 '16 at 19:42 on
source share

1 addition to the accepted answer that helped me make it work: when I put the specified text in a shell script, for some reason -e is saved. (likely because I'm too fat to work with shell scripts)

when I removed -e and moved the quotes to cover everything, it worked. SUBTREE2 = echo "040000 tree $SUBTREE1 modules" | git mktree echo "040000 tree $SUBTREE1 modules" | git mktree

Note that there is a tab between $ SUBTREE1 and the modules, which is the same \ t that -e should interpret.

+2
Jun 05 '13 at 14:15
source share

You can create a git repository in foo and specify both baz and bar via git submodules .

Then both bar and baz coexist while preserving their full history.




If you really only need one repo (foo), it has both the history of the bar and the history of baz, then the graft method or the fake merge strategy are fine.

0
Jul 09 '10 at 12:32
source share



All Articles