I have a local directory with multiple files. These files
are already stored in remote git (BitBucket).
I accidentally deleted these files locally, how can I
get it back from the remote repository?
You don't need to get them from remote if you already had those files locally.
You can use:
git checkout -- <deleted file name>
You can also use file and directory wildcards:
git checkout -- dir1/*
git checkout [<tree-ish>] [--] <pathspec>...
Overwrite paths in the working tree by replacing with the contents in the index or in the <tree-ish> (most often a commit). When a <tree-ish> is given, the paths that match the <pathspec> are updated both in the index and in the working tree.
Did you commit the deletions?
If not, you can use git checkout -- file to recover the files.
If you did, you can git reset to a previous commit where the files still existed.
If you need a more fine-tuned approach, you can git clone your remote repository to a new directory and then use file operations to copy files from the copy to your original repository.
You can reset your local master branch to the remote repo master like so:
git fetch
git reset --hard origin/master
If you just deleted the file and do not run git add, then
git checkout -- <file>
If you have run git add, then
git reset HEAD <file>
git checkout -- <file>
If you have commit it to local repository, then
git reset --hard <the_commit_before_bad_commit>
Anyway, you'd better backup all the files when you try it.
Related
This question already has answers here:
What's the -practical- difference between a Bare and non-Bare repository?
(11 answers)
Closed 2 years ago.
I have a linux server with ssh and git installed.
I created a folder on the root directory and changed its owner to mike to avoid giving out root passwords to anyone.
sudo mkdir /GitRepos
sudo chown mike:mike /GitRepos
Then I created an empty repo, and set it to bare, otherwise pushing to this repo does not work (I tried!!)
mkdir test.git
cd test.git
git init
git config core.bare true
I then used another machine (Windows) to clone the newly created repo:
git clone ssh://mike#myLinuxServer/GitRepos/test.git
and that worked fine, I got an empty repo as expected.
I added some files and committed the change.
git add .
git commit -m "Initial commit"
git push origin master
Pushing works fine. I even deleted the entire repo from the local machine, and tried to clone it again.
cd ..
rd test /s
git clone ssh://mike#myLinuxServer/GitRepos/test.git
It worked fine and I got all of the files.
When I went to the remote Linux machine, I listed the files inside test.git and found it empty.
Where did the files go?
How can the repo receive pushes and give out files for pulls without storing the files in the repo?
The files are stored in the remote repository's database. You cannot expect that the files themselves appear in the remote repository, in particular not when you declare it as bare.
The way you initialized the bare remote repository is very unusual. You should have done it simply like this:
cd /GitRepos
git init --bare test.git
The repository on the server is considered as a bare repo as you ran git config core.bare true. A more common way to initialize a bare repo is git init --bare test.git or mkdir test.git;cd test.git;git init --bare. The difference between git init test.git;cd test.git;git config core.bare true and git init --bare test.git lies in the directory structure. The former creates the worktree(test.git) and the database(test.git/.git) but forbids the worktree then. The latter creates only the database(test.git), without the worktree from the beginning.
A bare repository does not have an active worktree by default, which means you can't check out files from any revision in a bare repository. Although you can create extra worktrees with git worktree add, it's quite rare and may behave not so well as a non-bare repository with extra worktrees. All the revisions are stored as compressed data in the database. You can use commands to read the data, like git show master or git ls-tree -r master.
otherwise pushing to this repo does not work (I tried!!)
It's partially true. You can't push to a non-bare repository when you try to update a branch which has been checked out in the remote repository. If the branch you try to update is not checked out or does not exist yet, you can push to update or create it. With git config receive.denyCurrentBranch ignore or git config receive.denyCurrentBranch warn, you can even update such branch. But in practice, we always use bare repositories to preserve revision data.
I have two files file1.c and file1.h in my working directory /tmp/working. Everything I do is on my local file system.
I do a git init in /tmp/working that creates a .git directory in it. Then I git add file1.* and git commit -m "Feb 13th 2017".
On Feb 17 I accidentally delete the two files in my working directory. How do I restore my files in my working directory from my local git repository? I don't want to undo the last commit or something like that, just want the copy of my files (version of Feb 13th) back in my working directory.
You can try just checking out those two files from the latest commit:
git checkout -- path/to/file1.c
git checkout -- path/to/file1.h
The nice thing about Git is that it is hard to really mess things up. You only deleted those two files locally in the current commit. But their history is easily accessible using git checkout.
Actually, any path works:
git checkout -- path/to/ # extracts the whole "path/to" directory
git checkout -- . # extract all the content of the last commit
and you can also specify any commit :
git checkout other_branch -- path/to/ # extracts content from other_branch
git checkout v1.7.3 -- path/to/ # from this tag
git checkout eacf33b -- path/to/ # from this commit
Last week I created a Github repo and forgot to select a license for the repo. Now there are already 3 large commits.
I have asked the 3 contributors if it is ok if I delete the repo and then create it again with the same name and this time selecting the license when creating the repo, and they were fine with that.
Question
Is there a way I can get the commits into the new repo (this time the first commit is the LICENSE file) and still keep the commit meta info?
Is there a way I have get the commits into new repo (this time the first commit is the LICENSE file) and still keep the commit meta info?
Yes, by adding a remote and cherry-picking the commits on top of your first commit.
# add the old repo as a remote repository
git remote add oldrepo https://github.com/path/to/oldrepo
# get the old repo commits
git remote update
# examine the whole tree
git log --all --oneline --graph --decorate
# copy (cherry-pick) the commits from the old repo into your new local one
git cherry-pick sha-of-commit-one
git cherry-pick sha-of-commit-two
git cherry-pick sha-of-commit-three
# check your local repo is correct
git log
# send your new tree (repo state) to github
git push origin master
# remove the now-unneeded reference to oldrepo
git remote remove oldrepo
The rest of this answer is if you still want to add the LICENSE to your previous repo.
Yes. You can place your LICENSE commit as the first commit by rebasing.
Rebasing is gits way of rearranging commit order while keeping all the commit authors and commit dates intact.
When working on a shared repo, it's generally discouraged unless your entire team is git-fluent. For those that aren't, they can just clone a fresh copy of the repository.
Here's how you get your LICENSE commit as the first commit.
1. Update and rebase your local copy
Check out your project and place the LICENSE file in a commit ON TOP of your current 3 commit stack.
#create LICENSE file, edit, add content, save
git add LICENSE
git commit -m 'Initial commit'
Then do an interactive rebase on the master branch to REARRANGE the commits.
git rebase -i --root
It will open an editor. Move the bottom line (your "Initial commit" commit, the most recent commit) to the top of the file. Then save and quit the editor.
As soon as you exit the editor, git will write the commits in the order you just specified.
You now have your local copy of the repository updated. do:
git log
to verify.
2. Force push your new repo state to github
Now that your copy is updated, you have to force push it to github.
git push -f origin master
This will tell github to move the master branch to its new location.
You should only force push in rare occasions like this where everybody working with it is aware of the pending change, else it will confuse your collaborators.
3. Synchronize collaborators to github
Lastly, all the collaborators will have to synchronize to this repository.
First they must have clean repositories as the following command can be destructive if there are unsaved changes.
# make sure there are no unsaved changes
git status
# pull the latest version from github
git fetch
# move their master branch pointer to the one you published to github.
git reset --hard origin/master
That's it. Everybody should be in sync now.
I had a similar problem where I forgot to fork a repo to my github and added several commits before I realized my mistake.
I found a pretty simple solution.
First remove the remote to the original repo
git remote remove origin
Second add a remote to the new fork on my github
git remote add origin <my repo URL>
Then I pushed to origin master and all of my commits showed up on my github.
I used the following approach:
Clone the source repo to a folder like /c/SrcRepo
Clone the destination repo to a folder like /c/DstRepo and switch to the destination branch
In the root folder of the destination repo run the command:
git pull /c/SrcRepo srcBranch --allow-unrelated-histories
No necessary to create an additional remote reference
Based on #Moocowmoo's answer but trying to streamline it bit more
What this does differently is tries to avoid conflicts as much as possible, just assuming that the remote is correct.
It doesn't however handle deleted files well, so there is still a manual element.
# assuming you are already on the branch you want to be
git remote add oldrepo https://github.com/path/to/oldrepo
git fetch oldrepo
# take all or subset of changes from a branch
git cherry-pick --strategy recursive --strategy-option theirs oldestCommitHash^..latestCommitHash
# or take all changes included in a specific merge commit (easiest)
git cherry-pick --strategy recursive --strategy-option theirs mergeCommitHash^..mergeCommitHash
# handling deleted files/unhandled conflicts
# just keep repeating this section
git mergetool
# either c/m or d based on if you want to keep or delete the files
git cherry-pick --continue
Destination Git = UrlD (existing content doesn't matter)
SourceGit = UrlS
git clone UrlS
git remote add origin2 UrlD
git push -f origin2 master
Now the Destination will have the same data as Source(You can also use origin instead of origin2)
you can try this, it's easy and straightforward. This will push all commits before (and including) the hash you use as <last-commit-hash-from-old-repo> to the other repo:
git clone https://github.com/path/to/new-repo.git new-repo
cd new-repo
git remote add old https://github.com/path/to/old-repo.git
git remote update
git merge --allow-unrelated-histories <last-commit-hash-from-old-repo>
git push origin main
if anyone needs to push all commits from a repo to another as a single commit (like I needed), you can simply add --squash to the merge command like this:
git clone https://github.com/path/to/new-repo.git new-repo
cd new-repo
git remote add old https://github.com/path/to/old-repo.git
git remote update
git merge --squash --allow-unrelated-histories <last-commit-hash-from-old-repo>
git push origin main
In my case I needed to find out differences between the old and new repos. So in the new repo added the old one.
git remote add old https://gitlab.site.az/old-blog.git
Fetch all remotes
git fetch --all
Find different commits
git log --graph --oneline --pretty=format:"%h%x09%an%x09%ad%x09%s" --abbrev-commit --date=relative develop..old/develop
Get commits you selected
git cherry-pick SHA1 SHA2 SHA4
I'm following the documentation provided here by git to setup a bare git repository in a folder called root.
I started in the root directory where I ran
git init
git -A *
git commit -m "test"
I then ran git status and all appears good.
Next I ran the line from the documentation at a directory one level above the repo I created above.
git clone --bare root root.git
This created root.git but I cannot see any evidence that anything was cloned I just see a set of files and directories when I cd root.git.
I don't know how to verify it was actually cloned, and if it was why can't I see the original files?
When you do --bare --- you are telling git to clone just the git portion -
This is the option you use when you want to have a remote repository that does not include a workspace.
If you want to verify that it actually cloned your changes, you'll want to clone it again in a different directory - without the --bare flag
I would recommend using the full path to do this:
cd /path/to/some/workspace
git clone /path/to/your/root.git successful-git-clone #that last bit is optional
This will put the workspace contents of root.git into a folder named successful-git-clone/ - without that last bit, it will default to root/ -
Even if you are in a bare repository, some git commands works and you could do a git branch to see if you have all your branches or git log to look at your commits...
If you add the following script as a hooks/post-receive hook to a bare git repository foo.git:
#!/bin/sh
GIT_WORK_TREE=/bar git checkout -f
then whenever someone pushes to the repository the current state will be updated in directory bar.
This requires bar and foo.git to be on the same machine.
What's the easiest way to modify it so that the checkout is made on a remote machine (say baz:/bar) ?
One way would be to:
#!/bin/sh
GIT_WORK_TREE=/tmp/bar git checkout -f
rsync ... /tmp/bar baz:/bar
Is there a better way? Perhaps not requiring an intermediate temp directory? (If not what are the correct options to pass to rsync such that the resulting directory is indentical to being checked out directly?)
GIT_WORK_TREE=/bar means that bar is a git repo.
If bar is a git repo on the remote side, then it can pull from a bare repo bare_bar.git (also on the remote side), to which you can push to.
In other words, your post-receive hook would push to bare_bar repo through ssh, and a post-receive hook on that bare repo would trigger the pull from the actual repo bar: see "Creating a git repository from a production folder".
I'd prefer a solution that keeps the remote server free of any .git dirs (and even the git package itself ideally)
In that case, your current post-receive hook and its rsync command seems to be the only way to incrementally copy new data to a remote working tree.
git archive would archive everything each time.
git bundle would require git on the other side.